acumatica-cli 0.29.0__tar.gz → 0.30.0__tar.gz
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.
- {acumatica_cli-0.29.0 → acumatica_cli-0.30.0}/PKG-INFO +6 -10
- {acumatica_cli-0.29.0 → acumatica_cli-0.30.0}/README.md +5 -9
- {acumatica_cli-0.29.0 → acumatica_cli-0.30.0}/pyproject.toml +1 -1
- {acumatica_cli-0.29.0 → acumatica_cli-0.30.0}/pyproject.toml.orig +1 -1
- {acumatica_cli-0.29.0 → acumatica_cli-0.30.0}/src/acumatica_cli/cli.py +4 -192
- {acumatica_cli-0.29.0 → acumatica_cli-0.30.0}/src/acumatica_cli/templates/README.md +1 -2
- {acumatica_cli-0.29.0 → acumatica_cli-0.30.0}/src/acumatica_cli/__init__.py +0 -0
- {acumatica_cli-0.29.0 → acumatica_cli-0.30.0}/src/acumatica_cli/bootstrap.py +0 -0
- {acumatica_cli-0.29.0 → acumatica_cli-0.30.0}/src/acumatica_cli/bootstrap_plugin.cs +0 -0
- {acumatica_cli-0.29.0 → acumatica_cli-0.30.0}/src/acumatica_cli/bootstrap_project.xml +0 -0
- {acumatica_cli-0.29.0 → acumatica_cli-0.30.0}/src/acumatica_cli/client.py +0 -0
- {acumatica_cli-0.29.0 → acumatica_cli-0.30.0}/src/acumatica_cli/config.py +0 -0
- {acumatica_cli-0.29.0 → acumatica_cli-0.30.0}/src/acumatica_cli/extract.py +0 -0
- {acumatica_cli-0.29.0 → acumatica_cli-0.30.0}/src/acumatica_cli/firstlogin.py +0 -0
- {acumatica_cli-0.29.0 → acumatica_cli-0.30.0}/src/acumatica_cli/inventory.py +0 -0
- {acumatica_cli-0.29.0 → acumatica_cli-0.30.0}/src/acumatica_cli/models.py +0 -0
- {acumatica_cli-0.29.0 → acumatica_cli-0.30.0}/src/acumatica_cli/output.py +0 -0
- {acumatica_cli-0.29.0 → acumatica_cli-0.30.0}/src/acumatica_cli/reconcile.py +0 -0
- {acumatica_cli-0.29.0 → acumatica_cli-0.30.0}/src/acumatica_cli/run.py +0 -0
- {acumatica_cli-0.29.0 → acumatica_cli-0.30.0}/src/acumatica_cli/seed.py +0 -0
- {acumatica_cli-0.29.0 → acumatica_cli-0.30.0}/src/acumatica_cli/seed_catalog.yaml +0 -0
- {acumatica_cli-0.29.0 → acumatica_cli-0.30.0}/src/acumatica_cli/snapshot_map.yaml +0 -0
- {acumatica_cli-0.29.0 → acumatica_cli-0.30.0}/src/acumatica_cli/state.py +0 -0
- {acumatica_cli-0.29.0 → acumatica_cli-0.30.0}/src/acumatica_cli/templates/config/baseline/10-subaccounts.yaml +0 -0
- {acumatica_cli-0.29.0 → acumatica_cli-0.30.0}/src/acumatica_cli/templates/config/baseline/20-accounts.yaml +0 -0
- {acumatica_cli-0.29.0 → acumatica_cli-0.30.0}/src/acumatica_cli/templates/config/baseline/40-ledger.yaml +0 -0
- {acumatica_cli-0.29.0 → acumatica_cli-0.30.0}/src/acumatica_cli/templates/config/baseline/50-gl-preferences.yaml +0 -0
- {acumatica_cli-0.29.0 → acumatica_cli-0.30.0}/src/acumatica_cli/templates/config/baseline/60-ledger-company.yaml +0 -0
- {acumatica_cli-0.29.0 → acumatica_cli-0.30.0}/src/acumatica_cli/templates/config/baseline/90-uoms.yaml +0 -0
- {acumatica_cli-0.29.0 → acumatica_cli-0.30.0}/src/acumatica_cli/templates/config/baseline/91-company-packaging.yaml +0 -0
- {acumatica_cli-0.29.0 → acumatica_cli-0.30.0}/src/acumatica_cli/templates/config/bootstrap/company.yaml +0 -0
- {acumatica_cli-0.29.0 → acumatica_cli-0.30.0}/src/acumatica_cli/templates/config/bootstrap/credit-terms.yaml +0 -0
- {acumatica_cli-0.29.0 → acumatica_cli-0.30.0}/src/acumatica_cli/templates/config/bootstrap/features.yaml +0 -0
- {acumatica_cli-0.29.0 → acumatica_cli-0.30.0}/src/acumatica_cli/templates/config/bootstrap/segmented-key.yaml +0 -0
- {acumatica_cli-0.29.0 → acumatica_cli-0.30.0}/src/acumatica_cli/templates/config/master/05-numbering-sequences.yaml +0 -0
- {acumatica_cli-0.29.0 → acumatica_cli-0.30.0}/src/acumatica_cli/templates/config/master/10-reason-codes.yaml +0 -0
- {acumatica_cli-0.29.0 → acumatica_cli-0.30.0}/src/acumatica_cli/templates/config/master/20-in-preferences.yaml +0 -0
- {acumatica_cli-0.29.0 → acumatica_cli-0.30.0}/src/acumatica_cli/templates/config/master/30-availability-rules.yaml +0 -0
- {acumatica_cli-0.29.0 → acumatica_cli-0.30.0}/src/acumatica_cli/templates/config/master/40-posting-classes.yaml +0 -0
- {acumatica_cli-0.29.0 → acumatica_cli-0.30.0}/src/acumatica_cli/templates/config/master/50-warehouse.yaml +0 -0
- {acumatica_cli-0.29.0 → acumatica_cli-0.30.0}/src/acumatica_cli/templates/config/master/51-warehouse-locations.yaml +0 -0
- {acumatica_cli-0.29.0 → acumatica_cli-0.30.0}/src/acumatica_cli/templates/config/master/52-warehouse-defaults.yaml +0 -0
- {acumatica_cli-0.29.0 → acumatica_cli-0.30.0}/src/acumatica_cli/templates/config/master/53-tax-categories.yaml +0 -0
- {acumatica_cli-0.29.0 → acumatica_cli-0.30.0}/src/acumatica_cli/templates/config/master/54-item-classes.yaml +0 -0
- {acumatica_cli-0.29.0 → acumatica_cli-0.30.0}/src/acumatica_cli/templates/config/master/56-so-preferences.yaml +0 -0
- {acumatica_cli-0.29.0 → acumatica_cli-0.30.0}/src/acumatica_cli/templates/config/master/57-po-preferences.yaml +0 -0
- {acumatica_cli-0.29.0 → acumatica_cli-0.30.0}/src/acumatica_cli/templates/config/master/58-order-types.yaml +0 -0
- {acumatica_cli-0.29.0 → acumatica_cli-0.30.0}/src/acumatica_cli/templates/config/master/60-ar-preferences.yaml +0 -0
- {acumatica_cli-0.29.0 → acumatica_cli-0.30.0}/src/acumatica_cli/templates/config/master/61-ap-preferences.yaml +0 -0
- {acumatica_cli-0.29.0 → acumatica_cli-0.30.0}/src/acumatica_cli/templates/config/master/62-ca-preferences.yaml +0 -0
- {acumatica_cli-0.29.0 → acumatica_cli-0.30.0}/src/acumatica_cli/templates/config/master/63-cash-account.yaml +0 -0
- {acumatica_cli-0.29.0 → acumatica_cli-0.30.0}/src/acumatica_cli/templates/config/master/64-payment-methods.yaml +0 -0
- {acumatica_cli-0.29.0 → acumatica_cli-0.30.0}/src/acumatica_cli/templates/config/master/65-statement-cycles.yaml +0 -0
- {acumatica_cli-0.29.0 → acumatica_cli-0.30.0}/src/acumatica_cli/templates/config/master/70-vendor-classes.yaml +0 -0
- {acumatica_cli-0.29.0 → acumatica_cli-0.30.0}/src/acumatica_cli/templates/config/master/71-customer-classes.yaml +0 -0
- {acumatica_cli-0.29.0 → acumatica_cli-0.30.0}/src/acumatica_cli/templates/config/master/75-vendors.yaml +0 -0
- {acumatica_cli-0.29.0 → acumatica_cli-0.30.0}/src/acumatica_cli/templates/config/master/76-customers.yaml +0 -0
- {acumatica_cli-0.29.0 → acumatica_cli-0.30.0}/src/acumatica_cli/templates/config/master/80-stock-items-parts.yaml +0 -0
- {acumatica_cli-0.29.0 → acumatica_cli-0.30.0}/src/acumatica_cli/templates/config/master/82-stock-items-kits.yaml +0 -0
- {acumatica_cli-0.29.0 → acumatica_cli-0.30.0}/src/acumatica_cli/templates/config/master/85-kit-specifications.yaml +0 -0
- {acumatica_cli-0.29.0 → acumatica_cli-0.30.0}/src/acumatica_cli/templates/config/master/90-roles.yaml +0 -0
- {acumatica_cli-0.29.0 → acumatica_cli-0.30.0}/src/acumatica_cli/templates/config/master/91-users.yaml +0 -0
- {acumatica_cli-0.29.0 → acumatica_cli-0.30.0}/src/acumatica_cli/templates/config/master/92-role-users.yaml +0 -0
- {acumatica_cli-0.29.0 → acumatica_cli-0.30.0}/src/acumatica_cli/templates/config/setup/10-financial-year.yaml +0 -0
- {acumatica_cli-0.29.0 → acumatica_cli-0.30.0}/src/acumatica_cli/templates/config/setup/20-master-calendar.yaml +0 -0
- {acumatica_cli-0.29.0 → acumatica_cli-0.30.0}/src/acumatica_cli/templates/config/setup/30-open-periods.yaml +0 -0
- {acumatica_cli-0.29.0 → acumatica_cli-0.30.0}/src/acumatica_cli/templates/config/views/10-trial-balance.yaml +0 -0
- {acumatica_cli-0.29.0 → acumatica_cli-0.30.0}/src/acumatica_cli/templates/env +0 -0
- {acumatica_cli-0.29.0 → acumatica_cli-0.30.0}/src/acumatica_cli/templates/gitignore +0 -0
- {acumatica_cli-0.29.0 → acumatica_cli-0.30.0}/src/acumatica_cli/templates/overlays/README.md +0 -0
- {acumatica_cli-0.29.0 → acumatica_cli-0.30.0}/src/acumatica_cli/templates/overlays/default-24.200.001/README.md +0 -0
- {acumatica_cli-0.29.0 → acumatica_cli-0.30.0}/src/acumatica_cli/templates/overlays/default-24.200.001/scenario/30-build.yaml +0 -0
- {acumatica_cli-0.29.0 → acumatica_cli-0.30.0}/src/acumatica_cli/templates/scenario/10-seed-capital.yaml +0 -0
- {acumatica_cli-0.29.0 → acumatica_cli-0.30.0}/src/acumatica_cli/templates/scenario/20-buy.yaml +0 -0
- {acumatica_cli-0.29.0 → acumatica_cli-0.30.0}/src/acumatica_cli/templates/scenario/30-build.yaml +0 -0
- {acumatica_cli-0.29.0 → acumatica_cli-0.30.0}/src/acumatica_cli/templates/scenario/40-sell.yaml +0 -0
- {acumatica_cli-0.29.0 → acumatica_cli-0.30.0}/src/acumatica_cli/tenant.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: acumatica-cli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.30.0
|
|
4
4
|
Summary: Acumatica ERP Config-as-Code: tenant provisioning, baseline config, and reference data
|
|
5
5
|
Author: Konstantin Borovik
|
|
6
6
|
Author-email: Konstantin Borovik <kb@lab5.ca>
|
|
@@ -45,7 +45,7 @@ YAML in git is the source of truth; the live tenant is the target. `apply` is th
|
|
|
45
45
|
| Capture derived balances | `state` | Inquire trial-balance etc. into `state/` (not seed) |
|
|
46
46
|
| Create and destroy tenants | `tenant` | SSH control plane: list / create / delete / recycle |
|
|
47
47
|
| Publish the Bootstrap contract | `bootstrap` | REST publish of AcuBootstrap (or `--export` zip for the UI) |
|
|
48
|
-
| Prove a cold rebuild | `
|
|
48
|
+
| Prove a cold rebuild | `tenant create` then `apply` then `run` then `diff` | Compose on a fresh tenant. No wrap command. |
|
|
49
49
|
| Snapshot a site offline | `inventory` | SM203520 XML ZIP or `ac.exe export xml` writes `inventory/` |
|
|
50
50
|
| Cross-check snapshot vs seed | `reconcile` | `inventory/` plus optional `config/` writes `findings/` only |
|
|
51
51
|
| Dump the contract schema | `schema` | OpenAPI `swagger.json` for the pinned endpoint |
|
|
@@ -76,7 +76,6 @@ acu --tenant DEV apply config/ # seed config/{bootstrap,baseline,setup
|
|
|
76
76
|
acu --tenant DEV run scenario/ # once capital → buy → build → sell
|
|
77
77
|
acu --tenant DEV diff config/ # prove zero drift (exit 2 on drift)
|
|
78
78
|
acu --tenant DEV state # capture state/ trial-balance
|
|
79
|
-
acu check --yes --tenant DEV # cold lifecycle create→apply→run→diff (leave tenant)
|
|
80
79
|
```
|
|
81
80
|
|
|
82
81
|
Bare `apply` / `diff` (no path args) also prefer `config/` when those trees exist.
|
|
@@ -115,7 +114,6 @@ acu [--tenant NAME] [--url URL] [--ssh USER@HOST] [--api-version V]
|
|
|
115
114
|
├── apply [--dry-run] [FILES...] push YAML via REST (idempotent PUT upserts)
|
|
116
115
|
├── diff [FILES...] drift check vs the live tenant (exit 2 on drift)
|
|
117
116
|
├── run [--dry-run] [FILES...] execute transaction scenario YAML (exit 1 on any miss)
|
|
118
|
-
├── check [--yes] [--tenant L] cold lifecycle create→apply→run→diff; leave tenant (V47)
|
|
119
117
|
├── state [--out DIR] [--diff] [--assert-unchanged] [--dry-run] [FILES...]
|
|
120
118
|
│ capture derived state into state/ (not seed)
|
|
121
119
|
├── extract [--out DIR] [--only NAME]... [--force] [--dry-run]
|
|
@@ -285,7 +283,6 @@ Ad-hoc override: `acu --api-version 24.200.001 …` (version half only, never
|
|
|
285
283
|
`base_url` resolves as `--url`, else `ACU_BASE_URL`, else a hard error naming sources.
|
|
286
284
|
|
|
287
285
|
`acu config show` prints the resolved `.env` (password excluded; includes `ACU_API_VERSION`).
|
|
288
|
-
`acu check` (lifecycle) uses the same resolved instance.
|
|
289
286
|
|
|
290
287
|
### Multi-host overlays (V44)
|
|
291
288
|
|
|
@@ -317,9 +314,6 @@ acu diff
|
|
|
317
314
|
# explicit path args (no auto) — later path wins same keys
|
|
318
315
|
acu apply config/ overlays/default-24.200.001/
|
|
319
316
|
acu diff config/ overlays/default-24.200.001/
|
|
320
|
-
|
|
321
|
-
# cold lifecycle vs the resolved .env instance (SSH + tenant required)
|
|
322
|
-
acu check --yes --tenant LAB5
|
|
323
317
|
```
|
|
324
318
|
|
|
325
319
|
Add a future half by creating `overlays/default-<new-half>/` with the
|
|
@@ -335,7 +329,7 @@ Worth knowing:
|
|
|
335
329
|
- Without a `.env`, global flags plus the process environment supply the configuration.
|
|
336
330
|
- When `ACU_SSH` is **absent**, acu defaults to `Administrator@` + the resolved base_url hostname.
|
|
337
331
|
A **present blank** `ACU_SSH=` is the hosted opt-out.
|
|
338
|
-
Only `acu tenant`
|
|
332
|
+
Only `acu tenant` requires a non-empty value post-default.
|
|
339
333
|
- `acu config show` prints the resolved `.env` (password excluded).
|
|
340
334
|
- Redirect it to turn resolved state into a working config: `acu config show > .env`.
|
|
341
335
|
|
|
@@ -383,7 +377,9 @@ gmake release patch # or minor | major
|
|
|
383
377
|
2. Fail if `## Unreleased` has no bullets
|
|
384
378
|
3. Bump `pyproject.toml` version (`major` | `minor` | `patch`)
|
|
385
379
|
4. Promote Unreleased body to `## [vX.Y.Z] - YYYY-MM-DD`, leave an empty `## Unreleased`
|
|
386
|
-
5. Commit `CHANGELOG.md` + `pyproject.toml` (+ lock if bumped) together, tag `vX.Y.Z
|
|
380
|
+
5. Commit `CHANGELOG.md` + `pyproject.toml` (+ lock if bumped) together, tag `vX.Y.Z`
|
|
381
|
+
6. `gmake check` again on the promoted tree
|
|
382
|
+
7. Push tag + branch — GitHub Actions publishes
|
|
387
383
|
|
|
388
384
|
GitHub Actions on tag `v*` re-runs CI, builds sdist+wheel, publishes to PyPI via OIDC trusted publishing, and creates a GitHub Release whose notes are the promoted CHANGELOG section for that tag (plus the artifacts).
|
|
389
385
|
|
|
@@ -27,7 +27,7 @@ YAML in git is the source of truth; the live tenant is the target. `apply` is th
|
|
|
27
27
|
| Capture derived balances | `state` | Inquire trial-balance etc. into `state/` (not seed) |
|
|
28
28
|
| Create and destroy tenants | `tenant` | SSH control plane: list / create / delete / recycle |
|
|
29
29
|
| Publish the Bootstrap contract | `bootstrap` | REST publish of AcuBootstrap (or `--export` zip for the UI) |
|
|
30
|
-
| Prove a cold rebuild | `
|
|
30
|
+
| Prove a cold rebuild | `tenant create` then `apply` then `run` then `diff` | Compose on a fresh tenant. No wrap command. |
|
|
31
31
|
| Snapshot a site offline | `inventory` | SM203520 XML ZIP or `ac.exe export xml` writes `inventory/` |
|
|
32
32
|
| Cross-check snapshot vs seed | `reconcile` | `inventory/` plus optional `config/` writes `findings/` only |
|
|
33
33
|
| Dump the contract schema | `schema` | OpenAPI `swagger.json` for the pinned endpoint |
|
|
@@ -58,7 +58,6 @@ acu --tenant DEV apply config/ # seed config/{bootstrap,baseline,setup
|
|
|
58
58
|
acu --tenant DEV run scenario/ # once capital → buy → build → sell
|
|
59
59
|
acu --tenant DEV diff config/ # prove zero drift (exit 2 on drift)
|
|
60
60
|
acu --tenant DEV state # capture state/ trial-balance
|
|
61
|
-
acu check --yes --tenant DEV # cold lifecycle create→apply→run→diff (leave tenant)
|
|
62
61
|
```
|
|
63
62
|
|
|
64
63
|
Bare `apply` / `diff` (no path args) also prefer `config/` when those trees exist.
|
|
@@ -97,7 +96,6 @@ acu [--tenant NAME] [--url URL] [--ssh USER@HOST] [--api-version V]
|
|
|
97
96
|
├── apply [--dry-run] [FILES...] push YAML via REST (idempotent PUT upserts)
|
|
98
97
|
├── diff [FILES...] drift check vs the live tenant (exit 2 on drift)
|
|
99
98
|
├── run [--dry-run] [FILES...] execute transaction scenario YAML (exit 1 on any miss)
|
|
100
|
-
├── check [--yes] [--tenant L] cold lifecycle create→apply→run→diff; leave tenant (V47)
|
|
101
99
|
├── state [--out DIR] [--diff] [--assert-unchanged] [--dry-run] [FILES...]
|
|
102
100
|
│ capture derived state into state/ (not seed)
|
|
103
101
|
├── extract [--out DIR] [--only NAME]... [--force] [--dry-run]
|
|
@@ -267,7 +265,6 @@ Ad-hoc override: `acu --api-version 24.200.001 …` (version half only, never
|
|
|
267
265
|
`base_url` resolves as `--url`, else `ACU_BASE_URL`, else a hard error naming sources.
|
|
268
266
|
|
|
269
267
|
`acu config show` prints the resolved `.env` (password excluded; includes `ACU_API_VERSION`).
|
|
270
|
-
`acu check` (lifecycle) uses the same resolved instance.
|
|
271
268
|
|
|
272
269
|
### Multi-host overlays (V44)
|
|
273
270
|
|
|
@@ -299,9 +296,6 @@ acu diff
|
|
|
299
296
|
# explicit path args (no auto) — later path wins same keys
|
|
300
297
|
acu apply config/ overlays/default-24.200.001/
|
|
301
298
|
acu diff config/ overlays/default-24.200.001/
|
|
302
|
-
|
|
303
|
-
# cold lifecycle vs the resolved .env instance (SSH + tenant required)
|
|
304
|
-
acu check --yes --tenant LAB5
|
|
305
299
|
```
|
|
306
300
|
|
|
307
301
|
Add a future half by creating `overlays/default-<new-half>/` with the
|
|
@@ -317,7 +311,7 @@ Worth knowing:
|
|
|
317
311
|
- Without a `.env`, global flags plus the process environment supply the configuration.
|
|
318
312
|
- When `ACU_SSH` is **absent**, acu defaults to `Administrator@` + the resolved base_url hostname.
|
|
319
313
|
A **present blank** `ACU_SSH=` is the hosted opt-out.
|
|
320
|
-
Only `acu tenant`
|
|
314
|
+
Only `acu tenant` requires a non-empty value post-default.
|
|
321
315
|
- `acu config show` prints the resolved `.env` (password excluded).
|
|
322
316
|
- Redirect it to turn resolved state into a working config: `acu config show > .env`.
|
|
323
317
|
|
|
@@ -365,7 +359,9 @@ gmake release patch # or minor | major
|
|
|
365
359
|
2. Fail if `## Unreleased` has no bullets
|
|
366
360
|
3. Bump `pyproject.toml` version (`major` | `minor` | `patch`)
|
|
367
361
|
4. Promote Unreleased body to `## [vX.Y.Z] - YYYY-MM-DD`, leave an empty `## Unreleased`
|
|
368
|
-
5. Commit `CHANGELOG.md` + `pyproject.toml` (+ lock if bumped) together, tag `vX.Y.Z
|
|
362
|
+
5. Commit `CHANGELOG.md` + `pyproject.toml` (+ lock if bumped) together, tag `vX.Y.Z`
|
|
363
|
+
6. `gmake check` again on the promoted tree
|
|
364
|
+
7. Push tag + branch — GitHub Actions publishes
|
|
369
365
|
|
|
370
366
|
GitHub Actions on tag `v*` re-runs CI, builds sdist+wheel, publishes to PyPI via OIDC trusted publishing, and creates a GitHub Release whose notes are the promoted CHANGELOG section for that tag (plus the artifacts).
|
|
371
367
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "acumatica-cli"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.30.0"
|
|
4
4
|
description = "Acumatica ERP Config-as-Code: tenant provisioning, baseline config, and reference data"
|
|
5
5
|
authors = [{ name = "Konstantin Borovik", email = "kb@lab5.ca" }]
|
|
6
6
|
license = "PolyForm-Noncommercial-1.0.0"
|
|
@@ -55,7 +55,7 @@ COMMAND MAP (pick by intent)
|
|
|
55
55
|
seed write apply [--dry-run] [FILES...] (sole mutator)
|
|
56
56
|
seed check diff [FILES...] (exit 2 on drift)
|
|
57
57
|
txns run [--dry-run] [FILES...]
|
|
58
|
-
cold
|
|
58
|
+
cold rebuild tenant create then apply then run then diff (no wrap cmd)
|
|
59
59
|
pull seed extract [--only ENTITY]... (inverse of apply)
|
|
60
60
|
observe state [--diff|--assert-unchanged]
|
|
61
61
|
offline inventory ARTIFACT | reconcile
|
|
@@ -66,7 +66,6 @@ EXIT CODES (common)
|
|
|
66
66
|
0 success
|
|
67
67
|
1 operational / parse / expectation failure
|
|
68
68
|
2 seed drift (diff) or state moved (state --assert-unchanged)
|
|
69
|
-
check (lifecycle) never exits 2 — drift fails the run as exit 1
|
|
70
69
|
|
|
71
70
|
\b
|
|
72
71
|
DEFAULT PATHS (when FILES omitted)
|
|
@@ -674,8 +673,8 @@ def config_group() -> None:
|
|
|
674
673
|
show print resolved config as a complete .env (password redacted)
|
|
675
674
|
check read-only live preflight (REST + optional SSH)
|
|
676
675
|
|
|
677
|
-
|
|
678
|
-
|
|
676
|
+
Start here on a new machine: init → edit .env → config check →
|
|
677
|
+
tenant create / bootstrap.
|
|
679
678
|
"""
|
|
680
679
|
|
|
681
680
|
|
|
@@ -721,7 +720,6 @@ def config_init(host: str | None, directory: Path | None) -> None:
|
|
|
721
720
|
output.data(" 5. acu run scenario/")
|
|
722
721
|
output.data(" 6. acu diff config/")
|
|
723
722
|
output.data(" 7. acu state")
|
|
724
|
-
output.data(" 8. acu check # cold lifecycle (SSH + tenant)")
|
|
725
723
|
|
|
726
724
|
|
|
727
725
|
@config_group.command("show")
|
|
@@ -751,7 +749,7 @@ def config_check(ctx: click.Context) -> None:
|
|
|
751
749
|
"""Read-only preflight of the resolved target (ok/fail/warn/skip lines).
|
|
752
750
|
|
|
753
751
|
Proves connectivity before apply/run. Writes nothing (no PUT, no tenant
|
|
754
|
-
CRUD).
|
|
752
|
+
CRUD).
|
|
755
753
|
|
|
756
754
|
\b
|
|
757
755
|
Probe order
|
|
@@ -875,192 +873,6 @@ def _probe_endpoints(endpoints: list[tuple[str, str]], inst: Instance) -> bool:
|
|
|
875
873
|
return False
|
|
876
874
|
|
|
877
875
|
|
|
878
|
-
@cli.command("check")
|
|
879
|
-
@click.option(
|
|
880
|
-
"--yes",
|
|
881
|
-
is_flag=True,
|
|
882
|
-
help="Accepted for muscle-memory (pre-clean delete is always unattended)",
|
|
883
|
-
)
|
|
884
|
-
@click.option(
|
|
885
|
-
"--tenant",
|
|
886
|
-
"tenant_login",
|
|
887
|
-
default=None,
|
|
888
|
-
help="Tenant login for pre-clean create (else ACU_TENANT / global --tenant)",
|
|
889
|
-
)
|
|
890
|
-
@click.pass_context
|
|
891
|
-
def check_cmd(ctx: click.Context, yes: bool, tenant_login: str | None) -> None:
|
|
892
|
-
"""Cold lifecycle: delete → create → apply → run → diff (leave tenant).
|
|
893
|
-
|
|
894
|
-
Destructive rebuild for CI against the resolved .env instance. Not
|
|
895
|
-
`acu config check` (read-only preflight). Requires non-empty ACU_SSH
|
|
896
|
-
and a tenant login (--tenant / ACU_TENANT). Pre-clean delete is
|
|
897
|
-
always unattended; rebuilt tenant is left on the host for inspect /
|
|
898
|
-
state. --yes is accepted for muscle-memory only.
|
|
899
|
-
|
|
900
|
-
\b
|
|
901
|
-
Flow
|
|
902
|
-
delete tenant (if present) → create+bootstrap → apply → run → diff
|
|
903
|
-
Diff drift fails the run as exit 1 (never exit 2 from this command).
|
|
904
|
-
|
|
905
|
-
\b
|
|
906
|
-
Examples
|
|
907
|
-
acu check --tenant DEV
|
|
908
|
-
acu check --yes --tenant DEV
|
|
909
|
-
|
|
910
|
-
Prefer `config check` for "can I connect?" Prefer this for "rebuild
|
|
911
|
-
green from empty."
|
|
912
|
-
"""
|
|
913
|
-
_ = yes
|
|
914
|
-
overrides: dict[str, str] = dict(ctx.obj or {})
|
|
915
|
-
tenant = _resolve_check_tenant(tenant_login, overrides)
|
|
916
|
-
try:
|
|
917
|
-
inst = load_instance({**overrides, "tenant": tenant})
|
|
918
|
-
except SystemExit as exc:
|
|
919
|
-
raise SystemExit(str(exc)) from exc
|
|
920
|
-
if not inst.ssh:
|
|
921
|
-
raise SystemExit(
|
|
922
|
-
"ACU_SSH not set (pass --ssh, or put ACU_SSH in .env or the environment)"
|
|
923
|
-
)
|
|
924
|
-
output.data(f"check {inst.base_url} (api={inst.api_version})")
|
|
925
|
-
if _run_lifecycle(inst, tenant) != 0:
|
|
926
|
-
raise SystemExit(1)
|
|
927
|
-
output.success("check: green")
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
def _resolve_check_tenant(tenant_login: str | None, overrides: dict[str, str]) -> str:
|
|
931
|
-
"""Tenant login from flag, global, env, or .env; hard-error if unset."""
|
|
932
|
-
tenant = tenant_login or overrides.get("tenant") or os.environ.get("ACU_TENANT")
|
|
933
|
-
if not tenant and (root := find_data_root()) is not None:
|
|
934
|
-
tenant = read_env_values(root / ".env").get("tenant") or None
|
|
935
|
-
if not tenant:
|
|
936
|
-
raise SystemExit(
|
|
937
|
-
"tenant not set (pass --tenant, "
|
|
938
|
-
"or put ACU_TENANT in .env or the environment)"
|
|
939
|
-
)
|
|
940
|
-
return tenant
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
def _run_lifecycle(inst: Instance, tenant: str) -> int:
|
|
944
|
-
"""pre-clean→create→apply→run→diff; leave tenant (V47); return 0 or 1."""
|
|
945
|
-
mgr = TenantManager(inst)
|
|
946
|
-
_lifecycle_delete_best_effort(mgr, tenant, label="pre-clean")
|
|
947
|
-
try:
|
|
948
|
-
_lifecycle_create_and_bootstrap(inst, mgr, tenant)
|
|
949
|
-
data_inst = inst.model_copy(update={"tenant": tenant})
|
|
950
|
-
_lifecycle_apply(data_inst)
|
|
951
|
-
_lifecycle_run(data_inst)
|
|
952
|
-
_lifecycle_diff(data_inst)
|
|
953
|
-
except SystemExit as exc:
|
|
954
|
-
code = exc.code if isinstance(exc.code, int) else 1
|
|
955
|
-
if code == 2:
|
|
956
|
-
code = 1 # V47: check never exits 2
|
|
957
|
-
output.error(f"lifecycle: {exc}")
|
|
958
|
-
# Leave tenant for manual inspect (no post-clean — V47).
|
|
959
|
-
return 1 if code != 0 else 0
|
|
960
|
-
except (RuntimeError, httpx.HTTPError) as exc:
|
|
961
|
-
output.error(f"lifecycle: {_format_failure(exc, target=inst.base_url)}")
|
|
962
|
-
# Leave tenant for manual inspect (no post-clean — V47).
|
|
963
|
-
return 1
|
|
964
|
-
output.success(
|
|
965
|
-
f"lifecycle green on {inst.base_url} tenant {tenant} (left for inspection)"
|
|
966
|
-
)
|
|
967
|
-
return 0
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
def _lifecycle_create_and_bootstrap(
|
|
971
|
-
inst: Instance, mgr: TenantManager, tenant: str
|
|
972
|
-
) -> None:
|
|
973
|
-
"""Create (or exists-skip) tenant + init + bootstrap chain."""
|
|
974
|
-
tenants = mgr.list()
|
|
975
|
-
existing = next((t for t in tenants if t.login_name == tenant), None)
|
|
976
|
-
if existing is not None:
|
|
977
|
-
company_id = existing.company_id
|
|
978
|
-
output.data(f"skip create: tenant {tenant} exists (id {company_id})")
|
|
979
|
-
else:
|
|
980
|
-
company_id = max((t.company_id for t in tenants), default=0) + 1
|
|
981
|
-
with output.step(f"creating tenant {company_id} ({tenant}) on {inst.base_url}"):
|
|
982
|
-
mgr.create(company_id, tenant, 1, True, "")
|
|
983
|
-
output.data("created")
|
|
984
|
-
with output.step(f"aligning CompanyCD to login ({tenant})"):
|
|
985
|
-
if mgr.set_company_cd(company_id, tenant):
|
|
986
|
-
output.data(f"CompanyCD set to {tenant}")
|
|
987
|
-
else:
|
|
988
|
-
output.data(f"CompanyCD already {tenant}")
|
|
989
|
-
_init_tenant(inst, mgr, tenant)
|
|
990
|
-
_bootstrap_tenant(inst, mgr, tenant)
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
def _lifecycle_delete_best_effort(
|
|
994
|
-
mgr: TenantManager, tenant: str, *, label: str
|
|
995
|
-
) -> None:
|
|
996
|
-
try:
|
|
997
|
-
tenants = mgr.list()
|
|
998
|
-
if any(t.login_name == tenant for t in tenants):
|
|
999
|
-
with output.step(f"{label} delete tenant {tenant}"):
|
|
1000
|
-
mgr.delete(login_name=tenant)
|
|
1001
|
-
with output.step(f"recycling app pool after {label}"):
|
|
1002
|
-
mgr.recycle_app_pool()
|
|
1003
|
-
output.data(f"{label}: deleted {tenant}")
|
|
1004
|
-
else:
|
|
1005
|
-
output.data(f"{label}: tenant {tenant} absent")
|
|
1006
|
-
except (RuntimeError, SystemExit) as exc:
|
|
1007
|
-
output.warn(f"{label}: {exc}")
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
def _lifecycle_apply(inst: Instance) -> None:
|
|
1011
|
-
"""Bare apply (pin overlay auto-compose); SystemExit 1 on failure."""
|
|
1012
|
-
total_ok = 0
|
|
1013
|
-
all_errors: list[str] = []
|
|
1014
|
-
with AcumaticaClient(inst) as client:
|
|
1015
|
-
for path in expand_files(default_apply_dirs(inst)):
|
|
1016
|
-
baseline = seed.load_baseline(path)
|
|
1017
|
-
output.data(
|
|
1018
|
-
f"{path} -> {inst.tenant} on {inst.base_url} ({baseline.entity})"
|
|
1019
|
-
)
|
|
1020
|
-
n, errors = seed.apply(client, baseline, dry_run=False)
|
|
1021
|
-
total_ok += n
|
|
1022
|
-
all_errors.extend(f"{path}: {e}" for e in errors)
|
|
1023
|
-
output.data(f" {n} record(s)")
|
|
1024
|
-
if all_errors:
|
|
1025
|
-
output.error(f"apply: {len(all_errors)} error(s), {total_ok} record(s) applied")
|
|
1026
|
-
for err in all_errors:
|
|
1027
|
-
output.error(err)
|
|
1028
|
-
raise SystemExit(1)
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
def _lifecycle_run(inst: Instance) -> None:
|
|
1032
|
-
"""Bare run (pin overlay); SystemExit 1 on failure."""
|
|
1033
|
-
paths = list(default_scenario_files(inst))
|
|
1034
|
-
scenarios = [run.load_scenario(path) for path in paths]
|
|
1035
|
-
ok = True
|
|
1036
|
-
with AcumaticaClient(inst) as client:
|
|
1037
|
-
for scenario in scenarios:
|
|
1038
|
-
ok = run.run(client, scenario) and ok
|
|
1039
|
-
if not ok:
|
|
1040
|
-
raise SystemExit(1)
|
|
1041
|
-
output.success(f"{len(scenarios)} scenario(s) passed on {inst.tenant}")
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
def _lifecycle_diff(inst: Instance) -> None:
|
|
1045
|
-
"""Bare diff; SystemExit 2 on drift (caller maps to 1 for check)."""
|
|
1046
|
-
paths = expand_files(default_apply_dirs(inst))
|
|
1047
|
-
drifts: list[str] = []
|
|
1048
|
-
with AcumaticaClient(inst) as client:
|
|
1049
|
-
for path in paths:
|
|
1050
|
-
baseline = seed.load_baseline(path)
|
|
1051
|
-
output.data(
|
|
1052
|
-
f"{path} -> {inst.tenant} on {inst.base_url} ({baseline.entity})"
|
|
1053
|
-
)
|
|
1054
|
-
file_drifts = seed.diff(client, baseline)
|
|
1055
|
-
drifts += file_drifts
|
|
1056
|
-
n = 1 if isinstance(baseline, seed.ActionFile) else len(baseline.records)
|
|
1057
|
-
if file_drifts:
|
|
1058
|
-
output.data(f" {n} record(s), {len(file_drifts)} drift(s)")
|
|
1059
|
-
else:
|
|
1060
|
-
output.data(f" {n} record(s) ok")
|
|
1061
|
-
_exit_on_drift(inst, drifts, len(paths))
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
876
|
SEED_DIRS = ("bootstrap", "baseline", "setup", "master")
|
|
1065
877
|
|
|
1066
878
|
|
|
@@ -30,8 +30,7 @@ acu state
|
|
|
30
30
|
# warm gate: once-capital only — additive buy/sell moves numeric observations
|
|
31
31
|
acu run scenario/10-seed-capital.yaml && acu state --assert-unchanged
|
|
32
32
|
|
|
33
|
-
#
|
|
34
|
-
# acu check --yes
|
|
33
|
+
# SSH cold rebuild: acu tenant create --login NAME, then apply, run, diff
|
|
35
34
|
|
|
36
35
|
# Optional: re-seed from live (inverse of apply; always under config/)
|
|
37
36
|
# acu extract --out . --force
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{acumatica_cli-0.29.0 → acumatica_cli-0.30.0}/src/acumatica_cli/templates/overlays/README.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{acumatica_cli-0.29.0 → acumatica_cli-0.30.0}/src/acumatica_cli/templates/scenario/20-buy.yaml
RENAMED
|
File without changes
|
{acumatica_cli-0.29.0 → acumatica_cli-0.30.0}/src/acumatica_cli/templates/scenario/30-build.yaml
RENAMED
|
File without changes
|
{acumatica_cli-0.29.0 → acumatica_cli-0.30.0}/src/acumatica_cli/templates/scenario/40-sell.yaml
RENAMED
|
File without changes
|
|
File without changes
|