gen3-dataops-toolkit 4.3.0__tar.gz → 5.0.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.
Files changed (63) hide show
  1. {gen3_dataops_toolkit-4.3.0 → gen3_dataops_toolkit-5.0.0}/PKG-INFO +48 -4
  2. {gen3_dataops_toolkit-4.3.0 → gen3_dataops_toolkit-5.0.0}/README.md +47 -3
  3. {gen3_dataops_toolkit-4.3.0 → gen3_dataops_toolkit-5.0.0}/pyproject.toml +1 -1
  4. {gen3_dataops_toolkit-4.3.0 → gen3_dataops_toolkit-5.0.0}/src/g3dt/cli/config_cmds.py +52 -4
  5. {gen3_dataops_toolkit-4.3.0 → gen3_dataops_toolkit-5.0.0}/src/g3dt/cli/dict_cmds.py +108 -6
  6. {gen3_dataops_toolkit-4.3.0 → gen3_dataops_toolkit-5.0.0}/src/g3dt/cli/indexd_cmds.py +3 -1
  7. {gen3_dataops_toolkit-4.3.0 → gen3_dataops_toolkit-5.0.0}/src/g3dt/cli/jobs.py +24 -4
  8. {gen3_dataops_toolkit-4.3.0 → gen3_dataops_toolkit-5.0.0}/src/g3dt/cli/k8s.py +68 -10
  9. {gen3_dataops_toolkit-4.3.0 → gen3_dataops_toolkit-5.0.0}/src/g3dt/cli/main.py +71 -12
  10. {gen3_dataops_toolkit-4.3.0 → gen3_dataops_toolkit-5.0.0}/src/g3dt/cli/metadata.py +105 -8
  11. {gen3_dataops_toolkit-4.3.0 → gen3_dataops_toolkit-5.0.0}/src/g3dt/cli/pipeline_cmds.py +20 -4
  12. {gen3_dataops_toolkit-4.3.0 → gen3_dataops_toolkit-5.0.0}/src/g3dt/cli/study_cmds.py +131 -23
  13. {gen3_dataops_toolkit-4.3.0 → gen3_dataops_toolkit-5.0.0}/src/g3dt/cli/synth.py +47 -9
  14. {gen3_dataops_toolkit-4.3.0 → gen3_dataops_toolkit-5.0.0}/src/g3dt/services/dictionary/deploy_dd.sh +7 -1
  15. {gen3_dataops_toolkit-4.3.0 → gen3_dataops_toolkit-5.0.0}/src/g3dt/services/k8s_ops/argocd_restart_etl.sh +1 -1
  16. {gen3_dataops_toolkit-4.3.0 → gen3_dataops_toolkit-5.0.0}/src/g3dt/services/k8s_ops/argocd_restart_ms.sh +1 -1
  17. {gen3_dataops_toolkit-4.3.0 → gen3_dataops_toolkit-5.0.0}/src/g3dt/services/k8s_ops/argocd_restart_schema.sh +1 -1
  18. {gen3_dataops_toolkit-4.3.0 → gen3_dataops_toolkit-5.0.0}/src/g3dt/services/k8s_ops/restart_etl_and_ms.sh +13 -3
  19. {gen3_dataops_toolkit-4.3.0 → gen3_dataops_toolkit-5.0.0}/src/g3dt/services/synthetic_data/full_deploy_dd_and_synth.sh +9 -2
  20. {gen3_dataops_toolkit-4.3.0 → gen3_dataops_toolkit-5.0.0}/src/g3dt/services/upload/metadata/upload_all_studies.sh +8 -0
  21. {gen3_dataops_toolkit-4.3.0 → gen3_dataops_toolkit-5.0.0}/src/g3dt/services/upload/metadata/upload_metadata.py +55 -1
  22. {gen3_dataops_toolkit-4.3.0 → gen3_dataops_toolkit-5.0.0}/src/g3dt/__init__.py +0 -0
  23. {gen3_dataops_toolkit-4.3.0 → gen3_dataops_toolkit-5.0.0}/src/g3dt/cli/__init__.py +0 -0
  24. {gen3_dataops_toolkit-4.3.0 → gen3_dataops_toolkit-5.0.0}/src/g3dt/cli/_internal/__init__.py +0 -0
  25. {gen3_dataops_toolkit-4.3.0 → gen3_dataops_toolkit-5.0.0}/src/g3dt/cli/_internal/aws_quiet.py +0 -0
  26. {gen3_dataops_toolkit-4.3.0 → gen3_dataops_toolkit-5.0.0}/src/g3dt/cli/_internal/dispatch.py +0 -0
  27. {gen3_dataops_toolkit-4.3.0 → gen3_dataops_toolkit-5.0.0}/src/g3dt/cli/_internal/helptext.py +0 -0
  28. {gen3_dataops_toolkit-4.3.0 → gen3_dataops_toolkit-5.0.0}/src/g3dt/cli/_internal/registry.py +0 -0
  29. {gen3_dataops_toolkit-4.3.0 → gen3_dataops_toolkit-5.0.0}/src/g3dt/cli/_internal/resolve.py +0 -0
  30. {gen3_dataops_toolkit-4.3.0 → gen3_dataops_toolkit-5.0.0}/src/g3dt/cli/_internal/runner.py +0 -0
  31. {gen3_dataops_toolkit-4.3.0 → gen3_dataops_toolkit-5.0.0}/src/g3dt/cli/_internal/safety.py +0 -0
  32. {gen3_dataops_toolkit-4.3.0 → gen3_dataops_toolkit-5.0.0}/src/g3dt/cli/delete_cmds.py +0 -0
  33. {gen3_dataops_toolkit-4.3.0 → gen3_dataops_toolkit-5.0.0}/src/g3dt/cli/ec2_cmds.py +0 -0
  34. {gen3_dataops_toolkit-4.3.0 → gen3_dataops_toolkit-5.0.0}/src/g3dt/cli/release_cmds.py +0 -0
  35. {gen3_dataops_toolkit-4.3.0 → gen3_dataops_toolkit-5.0.0}/src/g3dt/config.py +0 -0
  36. {gen3_dataops_toolkit-4.3.0 → gen3_dataops_toolkit-5.0.0}/src/g3dt/contexts.py +0 -0
  37. {gen3_dataops_toolkit-4.3.0 → gen3_dataops_toolkit-5.0.0}/src/g3dt/import_order.py +0 -0
  38. {gen3_dataops_toolkit-4.3.0 → gen3_dataops_toolkit-5.0.0}/src/g3dt/indexd/__init__.py +0 -0
  39. {gen3_dataops_toolkit-4.3.0 → gen3_dataops_toolkit-5.0.0}/src/g3dt/indexd/file_access.py +0 -0
  40. {gen3_dataops_toolkit-4.3.0 → gen3_dataops_toolkit-5.0.0}/src/g3dt/indexd/indexd_registrar.py +0 -0
  41. {gen3_dataops_toolkit-4.3.0 → gen3_dataops_toolkit-5.0.0}/src/g3dt/ingest/ingest.py +0 -0
  42. {gen3_dataops_toolkit-4.3.0 → gen3_dataops_toolkit-5.0.0}/src/g3dt/resolver.py +0 -0
  43. {gen3_dataops_toolkit-4.3.0 → gen3_dataops_toolkit-5.0.0}/src/g3dt/services/delete/delete_all_metadata_for_project.py +0 -0
  44. {gen3_dataops_toolkit-4.3.0 → gen3_dataops_toolkit-5.0.0}/src/g3dt/services/delete/delete_metadata.sh +0 -0
  45. {gen3_dataops_toolkit-4.3.0 → gen3_dataops_toolkit-5.0.0}/src/g3dt/services/delete/delete_metadata_by_guid.py +0 -0
  46. {gen3_dataops_toolkit-4.3.0 → gen3_dataops_toolkit-5.0.0}/src/g3dt/services/delete/delete_synth_metadata_by_version.py +0 -0
  47. {gen3_dataops_toolkit-4.3.0 → gen3_dataops_toolkit-5.0.0}/src/g3dt/services/dictionary/pull_dict.sh +0 -0
  48. {gen3_dataops_toolkit-4.3.0 → gen3_dataops_toolkit-5.0.0}/src/g3dt/services/dictionary/upload_dictionary.py +0 -0
  49. {gen3_dataops_toolkit-4.3.0 → gen3_dataops_toolkit-5.0.0}/src/g3dt/services/indexd/register_indexd.py +0 -0
  50. {gen3_dataops_toolkit-4.3.0 → gen3_dataops_toolkit-5.0.0}/src/g3dt/services/indexd/verify_file_access.py +0 -0
  51. {gen3_dataops_toolkit-4.3.0 → gen3_dataops_toolkit-5.0.0}/src/g3dt/services/k8s_ops/login_to_pod.sh +0 -0
  52. {gen3_dataops_toolkit-4.3.0 → gen3_dataops_toolkit-5.0.0}/src/g3dt/services/synthetic_data/delete_synth_metadata_sheepdog.py +0 -0
  53. {gen3_dataops_toolkit-4.3.0 → gen3_dataops_toolkit-5.0.0}/src/g3dt/services/synthetic_data/generate_synth_metadata.sh +0 -0
  54. {gen3_dataops_toolkit-4.3.0 → gen3_dataops_toolkit-5.0.0}/src/g3dt/services/synthetic_data/upload_synth_metadata_sheepdog.py +0 -0
  55. {gen3_dataops_toolkit-4.3.0 → gen3_dataops_toolkit-5.0.0}/src/g3dt/studies.py +0 -0
  56. {gen3_dataops_toolkit-4.3.0 → gen3_dataops_toolkit-5.0.0}/src/g3dt/upload/__init__.py +0 -0
  57. {gen3_dataops_toolkit-4.3.0 → gen3_dataops_toolkit-5.0.0}/src/g3dt/upload/metadata_deleter.py +0 -0
  58. {gen3_dataops_toolkit-4.3.0 → gen3_dataops_toolkit-5.0.0}/src/g3dt/upload/metadata_submitter.py +0 -0
  59. {gen3_dataops_toolkit-4.3.0 → gen3_dataops_toolkit-5.0.0}/src/g3dt/upload/upload_synthdata_s3.py +0 -0
  60. {gen3_dataops_toolkit-4.3.0 → gen3_dataops_toolkit-5.0.0}/src/g3dt/utils/athena_utils.py +0 -0
  61. {gen3_dataops_toolkit-4.3.0 → gen3_dataops_toolkit-5.0.0}/src/g3dt/utils/dbt_utils.py +0 -0
  62. {gen3_dataops_toolkit-4.3.0 → gen3_dataops_toolkit-5.0.0}/src/g3dt/utils/release_writer.py +0 -0
  63. {gen3_dataops_toolkit-4.3.0 → gen3_dataops_toolkit-5.0.0}/src/g3dt/validate/validate.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: gen3-dataops-toolkit
3
- Version: 4.3.0
3
+ Version: 5.0.0
4
4
  Summary: Gen3 DataOps toolkit (g3dt): operate SSM-published Gen3 data pipeline environments
5
5
  License: Apache-2.0
6
6
  Author: JoshuaHarris391
@@ -37,7 +37,7 @@ Description-Content-Type: text/markdown
37
37
  Operate Gen3 AWS data-pipeline environments from one pip-installable CLI.
38
38
 
39
39
  `g3dt` is the tooling half of the Gen3 DataOps platform: the
40
- [gen3-aws-data-pipeline](https://github.com/AustralianBioCommons/gen3-aws-data-pipeline)
40
+ [aws-gen3-pipeline](https://github.com/AustralianBioCommons/aws-gen3-pipeline)
41
41
  CDK app deploys a complete pipeline per project/environment and publishes every
42
42
  resource name to AWS SSM Parameter Store; `g3dt` resolves those names at
43
43
  runtime and gives operators one command surface for dictionary deploys,
@@ -53,9 +53,15 @@ operates any project: it is targeted by a **context** — a named
53
53
  ## Install
54
54
 
55
55
  ```bash
56
- pip install gen3-dataops-toolkit
56
+ pipx install gen3-dataops-toolkit # a CLI in its own venv (recommended)
57
+ pip install gen3-dataops-toolkit # or into an environment you manage
57
58
  ```
58
59
 
60
+ Synthetic-data generation needs the simulator too: `g3dt synth
61
+ install-simulator` installs it into whichever environment owns `g3dt`
62
+ (for pipx that is `pipx inject gen3-dataops-toolkit gen3-metadata-simulator`).
63
+ `g3dt --version` reports the installed toolkit version.
64
+
59
65
  ## Bootstrap: discover, then use
60
66
 
61
67
  ```bash
@@ -111,7 +117,7 @@ CodeBuild's `g3dt config dbt-env --env $ENV` contract is unchanged.)
111
117
  There are exactly two kinds of configuration:
112
118
 
113
119
  - **INPUTS** — human-authored values, committed as
114
- `config/<projectId>.<env>.json` in the CDK repo and read only by
120
+ `config/<project>.<env>.json` in the deployment wrapper repo and read only by
115
121
  `cdk deploy`. To change what an environment *declares*, edit that file and
116
122
  redeploy — the value flows to SSM.
117
123
  - **OUTPUTS** — every resource name the CDK creates plus the mirrored Gen3
@@ -169,6 +175,35 @@ gate fails -> inspect the results table -> fix data -> re-run until green.
169
175
  multi-study caller resolves the schema once, lists the validation prefix
170
176
  once, and batches all studies into a single Iceberg INSERT.
171
177
 
178
+ ### The study registry: what an upload sends, and from where
179
+
180
+ Each study the pipeline can upload is one JSON record in the env's SSM tree
181
+ (`/{project}/{env}/studies/<name>`) holding its Gen3 `project_id`,
182
+ `program_id` and `s3_metadata_path` — the `s3://` prefix of that study's
183
+ release JSONs (`DataImportOrder.txt` plus one `<node>.json` per node, exactly
184
+ as the release export writes them). The environment lives in the path, so
185
+ staging and prod records can never cross-resolve. `g3dt config` shows the
186
+ registry but never edits deployed settings; the registry is the one thing the
187
+ toolkit itself writes, through `g3dt study` (design: `docs/design/studies.md`):
188
+
189
+ ```bash
190
+ g3dt study list # bare names (config studies is an alias)
191
+ g3dt study show cdah # record + liveness check of its S3 path
192
+ g3dt study add cdah --project-id CAUGHT-CAD --program-id program1 \
193
+ --path s3://my-gold/release_jsons/v2.0.0/cdah/
194
+ g3dt study set cdah --path s3://my-gold/release_jsons/v2.1.0/cdah/ --dry-run
195
+ g3dt study set cdah --path s3://my-gold/release_jsons/v2.1.0/cdah/ # config study-set is an alias
196
+ g3dt study repoint --release v2.1.0 # every study at once (--latest: newest tag)
197
+ g3dt study remove cdah # registry entry only; data untouched
198
+ ```
199
+
200
+ `set` and `repoint` check a new path in S3 before writing anything (the same
201
+ `DataImportOrder.txt` + node-JSON checks `metadata upload` makes), so a typo
202
+ cannot point an upload at nothing; `set --no-verify` records a path whose
203
+ release is not exported yet. Every write on a production context requires
204
+ typing the context name. To upload one release without moving the registry,
205
+ use `g3dt metadata upload --release <tag>` (5.0.0).
206
+
172
207
  ### Where the data dictionary comes from
173
208
 
174
209
  Composed from the env's inputs as
@@ -288,6 +323,15 @@ one run, and environments deployed without the block keep the classic Gen3 set
288
323
  a manually redeployed frontend) simply omits it from the list in its wrapper
289
324
  config. `g3dt config show --env <env>` prints the resolved values.
290
325
 
326
+ **Syncing ArgoCD is opt-in.** None of these flows runs `argocd app sync`
327
+ unless you pass `--sync` (available on `k8s restart-schema`, `k8s restart-etl`,
328
+ `k8s restart-ms`, `dict deploy` and `synth deploy`). Add it only when the
329
+ commons app is behind the merged revision; a sync that fails on unrelated
330
+ drift (for example a Job whose template changed, which Kubernetes refuses to
331
+ patch) aborts the whole command before anything restarts. Before 5.0.0
332
+ `restart-ms` and `synth deploy` always synced first, and `restart-ms` ignored
333
+ `--restart-services`; both are fixed.
334
+
291
335
  ## Verifying download access (check-download)
292
336
 
293
337
  Registration alone does not prove a file can be downloaded. Two failure modes
@@ -3,7 +3,7 @@
3
3
  Operate Gen3 AWS data-pipeline environments from one pip-installable CLI.
4
4
 
5
5
  `g3dt` is the tooling half of the Gen3 DataOps platform: the
6
- [gen3-aws-data-pipeline](https://github.com/AustralianBioCommons/gen3-aws-data-pipeline)
6
+ [aws-gen3-pipeline](https://github.com/AustralianBioCommons/aws-gen3-pipeline)
7
7
  CDK app deploys a complete pipeline per project/environment and publishes every
8
8
  resource name to AWS SSM Parameter Store; `g3dt` resolves those names at
9
9
  runtime and gives operators one command surface for dictionary deploys,
@@ -19,9 +19,15 @@ operates any project: it is targeted by a **context** — a named
19
19
  ## Install
20
20
 
21
21
  ```bash
22
- pip install gen3-dataops-toolkit
22
+ pipx install gen3-dataops-toolkit # a CLI in its own venv (recommended)
23
+ pip install gen3-dataops-toolkit # or into an environment you manage
23
24
  ```
24
25
 
26
+ Synthetic-data generation needs the simulator too: `g3dt synth
27
+ install-simulator` installs it into whichever environment owns `g3dt`
28
+ (for pipx that is `pipx inject gen3-dataops-toolkit gen3-metadata-simulator`).
29
+ `g3dt --version` reports the installed toolkit version.
30
+
25
31
  ## Bootstrap: discover, then use
26
32
 
27
33
  ```bash
@@ -77,7 +83,7 @@ CodeBuild's `g3dt config dbt-env --env $ENV` contract is unchanged.)
77
83
  There are exactly two kinds of configuration:
78
84
 
79
85
  - **INPUTS** — human-authored values, committed as
80
- `config/<projectId>.<env>.json` in the CDK repo and read only by
86
+ `config/<project>.<env>.json` in the deployment wrapper repo and read only by
81
87
  `cdk deploy`. To change what an environment *declares*, edit that file and
82
88
  redeploy — the value flows to SSM.
83
89
  - **OUTPUTS** — every resource name the CDK creates plus the mirrored Gen3
@@ -135,6 +141,35 @@ gate fails -> inspect the results table -> fix data -> re-run until green.
135
141
  multi-study caller resolves the schema once, lists the validation prefix
136
142
  once, and batches all studies into a single Iceberg INSERT.
137
143
 
144
+ ### The study registry: what an upload sends, and from where
145
+
146
+ Each study the pipeline can upload is one JSON record in the env's SSM tree
147
+ (`/{project}/{env}/studies/<name>`) holding its Gen3 `project_id`,
148
+ `program_id` and `s3_metadata_path` — the `s3://` prefix of that study's
149
+ release JSONs (`DataImportOrder.txt` plus one `<node>.json` per node, exactly
150
+ as the release export writes them). The environment lives in the path, so
151
+ staging and prod records can never cross-resolve. `g3dt config` shows the
152
+ registry but never edits deployed settings; the registry is the one thing the
153
+ toolkit itself writes, through `g3dt study` (design: `docs/design/studies.md`):
154
+
155
+ ```bash
156
+ g3dt study list # bare names (config studies is an alias)
157
+ g3dt study show cdah # record + liveness check of its S3 path
158
+ g3dt study add cdah --project-id CAUGHT-CAD --program-id program1 \
159
+ --path s3://my-gold/release_jsons/v2.0.0/cdah/
160
+ g3dt study set cdah --path s3://my-gold/release_jsons/v2.1.0/cdah/ --dry-run
161
+ g3dt study set cdah --path s3://my-gold/release_jsons/v2.1.0/cdah/ # config study-set is an alias
162
+ g3dt study repoint --release v2.1.0 # every study at once (--latest: newest tag)
163
+ g3dt study remove cdah # registry entry only; data untouched
164
+ ```
165
+
166
+ `set` and `repoint` check a new path in S3 before writing anything (the same
167
+ `DataImportOrder.txt` + node-JSON checks `metadata upload` makes), so a typo
168
+ cannot point an upload at nothing; `set --no-verify` records a path whose
169
+ release is not exported yet. Every write on a production context requires
170
+ typing the context name. To upload one release without moving the registry,
171
+ use `g3dt metadata upload --release <tag>` (5.0.0).
172
+
138
173
  ### Where the data dictionary comes from
139
174
 
140
175
  Composed from the env's inputs as
@@ -254,6 +289,15 @@ one run, and environments deployed without the block keep the classic Gen3 set
254
289
  a manually redeployed frontend) simply omits it from the list in its wrapper
255
290
  config. `g3dt config show --env <env>` prints the resolved values.
256
291
 
292
+ **Syncing ArgoCD is opt-in.** None of these flows runs `argocd app sync`
293
+ unless you pass `--sync` (available on `k8s restart-schema`, `k8s restart-etl`,
294
+ `k8s restart-ms`, `dict deploy` and `synth deploy`). Add it only when the
295
+ commons app is behind the merged revision; a sync that fails on unrelated
296
+ drift (for example a Job whose template changed, which Kubernetes refuses to
297
+ patch) aborts the whole command before anything restarts. Before 5.0.0
298
+ `restart-ms` and `synth deploy` always synced first, and `restart-ms` ignored
299
+ `--restart-services`; both are fixed.
300
+
257
301
  ## Verifying download access (check-download)
258
302
 
259
303
  Registration alone does not prove a file can be downloaded. Two failure modes
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "gen3-dataops-toolkit"
3
- version = "4.3.0"
3
+ version = "5.0.0"
4
4
  description = "Gen3 DataOps toolkit (g3dt): operate SSM-published Gen3 data pipeline environments"
5
5
  authors = ["JoshuaHarris391 <harjo391@gmail.com>"]
6
6
  readme = "README.md"
@@ -24,6 +24,7 @@ from typing import List, Optional
24
24
  import typer
25
25
 
26
26
  from g3dt import config, contexts
27
+ from g3dt.cli import study_cmds
27
28
  from g3dt.cli._internal import resolve
28
29
  from g3dt.cli._internal.resolve import env_of, study_of
29
30
  from g3dt.cli._internal.helptext import ENV_OPT
@@ -35,8 +36,11 @@ app = typer.Typer(
35
36
  "stored in the local g3dt.yaml marker. Every command acts on the "
36
37
  "current context — switch with 'g3dt config use <name>', one-shot "
37
38
  "with --ctx. Discovery scans one SSO profile's AWS account at a "
38
- "time ('g3dt config discover <profile>'). Run 'g3dt docs' for the "
39
- "full model.",
39
+ "time ('g3dt config discover <profile>'). Deployed settings are "
40
+ "read-only here (they are CDK inputs); study records are edited "
41
+ "with 'g3dt config study-set' / 'g3dt study set' and moved to a "
42
+ "release with 'g3dt study repoint'. Run 'g3dt docs' for the full "
43
+ "model.",
40
44
  )
41
45
 
42
46
 
@@ -530,12 +534,51 @@ def envs() -> None:
530
534
  def studies(
531
535
  env: Optional[str] = typer.Option(None, "--env", "-e", help=ENV_OPT),
532
536
  ) -> None:
533
- """List the env's registered studies (alias of `g3dt study list`)."""
534
- from g3dt.cli import study_cmds
537
+ """List the env's registered studies (alias of `g3dt study list`).
535
538
 
539
+ Inspect one with `g3dt config show --study <name>`; change its S3 path
540
+ or Gen3 ids with `g3dt config study-set <name> ...`.
541
+ """
536
542
  study_cmds.list_impl(env)
537
543
 
538
544
 
545
+ @app.command("study-set")
546
+ def study_set(
547
+ name: str = typer.Argument(..., help="Study name (see `g3dt config studies`)."),
548
+ env: Optional[str] = typer.Option(None, "--env", "-e", help=ENV_OPT),
549
+ project_id: Optional[str] = typer.Option(
550
+ None, "--project-id", help=study_cmds.SET_PROJECT_ID_HELP
551
+ ),
552
+ program_id: Optional[str] = typer.Option(
553
+ None, "--program-id", help=study_cmds.SET_PROGRAM_ID_HELP
554
+ ),
555
+ path: Optional[str] = typer.Option(None, "--path", help=study_cmds.SET_PATH_HELP),
556
+ dry_run: bool = typer.Option(
557
+ False, "--dry-run", "-d", help=study_cmds.SET_DRY_RUN_HELP
558
+ ),
559
+ no_verify: bool = typer.Option(
560
+ False, "--no-verify", help=study_cmds.SET_NO_VERIFY_HELP
561
+ ),
562
+ ) -> None:
563
+ """Update a study's S3 release location or Gen3 ids (alias of `g3dt study set`).
564
+
565
+ Only the fields you pass change. A new --path is checked in S3 first
566
+ (DataImportOrder.txt plus node JSONs) so an upload can never be pointed
567
+ at nothing. To move every study to a new release at once use
568
+ `g3dt study repoint`.
569
+
570
+ Examples:
571
+ g3dt config study-set cdah --path s3://my-gold/release_jsons/v2.1.0/cdah/ --dry-run
572
+ g3dt config study-set cdah --path s3://my-gold/release_jsons/v2.1.0/cdah/
573
+
574
+ Targeting production requires typing the context/env name to confirm.
575
+ """
576
+ study_cmds.set_impl(
577
+ name, env, project_id, program_id, path,
578
+ dry_run=dry_run, verify=not no_verify,
579
+ )
580
+
581
+
539
582
  @app.command()
540
583
  def show(
541
584
  env: Optional[str] = typer.Option(None, "--env", "-e", help=ENV_OPT),
@@ -586,6 +629,11 @@ def show(
586
629
  typer.echo(f" project_id : {s.project_id}")
587
630
  typer.echo(f" program_id : {s.program_id}")
588
631
  typer.echo(f" s3_metadata_path : {s.s3_metadata_path}")
632
+ typer.secho(
633
+ f" change with : g3dt config study-set {s.key} --path s3://... "
634
+ f"| g3dt study repoint --release <tag>",
635
+ fg=typer.colors.BRIGHT_BLACK,
636
+ )
589
637
  if full:
590
638
  rc = resolve.rc_of(env)
591
639
  typer.secho(
@@ -14,12 +14,21 @@ from typing import Optional
14
14
 
15
15
  import typer
16
16
 
17
- from g3dt.config import dictionary_filename, dictionary_url, script_env
17
+ from g3dt.config import (
18
+ ConfigError,
19
+ dictionary_filename,
20
+ dictionary_url,
21
+ normalize_s3_location,
22
+ script_env,
23
+ )
18
24
  from g3dt.cli._internal import resolve, runner, safety
19
25
  from g3dt.cli._internal.resolve import env_of
20
26
  from g3dt.cli._internal.helptext import ENV_OPT
21
27
 
22
- app = typer.Typer(no_args_is_help=True, help="Data dictionary operations (local).")
28
+ app = typer.Typer(
29
+ no_args_is_help=True,
30
+ help="Data dictionary operations: pull, upload, deploy, status (local).",
31
+ )
23
32
 
24
33
  SCHEMA_DIR = Path("~/.g3dt/schemas").expanduser()
25
34
 
@@ -78,6 +87,82 @@ def pull(
78
87
  )
79
88
 
80
89
 
90
+ @app.command()
91
+ def status(
92
+ env: Optional[str] = typer.Option(None, "--env", "-e", help=ENV_OPT),
93
+ strict: bool = typer.Option(
94
+ False, "--strict",
95
+ help="Exit 1 when the deployed version differs from the declared one.",
96
+ ),
97
+ ) -> None:
98
+ """Show which dictionary version is deployed, next to the declared one.
99
+
100
+ Declared is the env's `dictionary_version` (a CDK input — what
101
+ `g3dt config show` reports). Deployed is the `version` stamped on the S3
102
+ object at `schema_s3_uri` by `dict upload`, i.e. what the commons'
103
+ services actually read on their next restart. The two drift after
104
+ `dict deploy --version <tag>` until the CDK config is updated and
105
+ redeployed (`g3dt config diff` shows that half). Read-only.
106
+
107
+ Examples:
108
+ g3dt dict status --env staging
109
+ g3dt dict status --env staging --strict # drift gate for scripts
110
+ """
111
+ from botocore.exceptions import ClientError
112
+
113
+ env = resolve.active_env(env)
114
+ e = env_of(env)
115
+ _, session = resolve.rc_session_of(env)
116
+ try:
117
+ location = normalize_s3_location(e.schema_s3_uri, param="schema_s3_uri")
118
+ except ConfigError as exc:
119
+ typer.secho(str(exc), fg=typer.colors.RED, err=True)
120
+ raise typer.Exit(1)
121
+ if "/" not in location:
122
+ typer.secho(
123
+ f"schema_s3_uri '{e.schema_s3_uri}' has no object key — expected "
124
+ f"<bucket>/<key>.", fg=typer.colors.RED, err=True,
125
+ )
126
+ raise typer.Exit(1)
127
+ bucket, key = location.split("/", 1)
128
+ deployed, uploaded = None, None
129
+ try:
130
+ head = session.client("s3").head_object(Bucket=bucket, Key=key)
131
+ deployed = head.get("Metadata", {}).get("version")
132
+ uploaded = head.get("LastModified")
133
+ except ClientError as exc:
134
+ code = exc.response.get("Error", {}).get("Code", "")
135
+ if code not in ("404", "NoSuchKey", "NotFound"):
136
+ typer.secho(
137
+ f"Cannot read s3://{location} ({code or exc}).",
138
+ fg=typer.colors.RED, err=True,
139
+ )
140
+ raise typer.Exit(1)
141
+
142
+ declared = e.dictionary_version
143
+ typer.echo(f"declared : {declared} (SSM app/dictionary_version)")
144
+ if uploaded is None:
145
+ typer.echo(f"deployed : (nothing at s3://{location} — run `g3dt dict deploy`)")
146
+ state = "MISSING"
147
+ else:
148
+ stamp = deployed or "(no version stamp on the object)"
149
+ when = uploaded.strftime("%Y-%m-%d %H:%M %Z") if hasattr(uploaded, "strftime") else uploaded
150
+ typer.echo(f"deployed : {stamp} (s3://{location}, uploaded {when})")
151
+ state = "in sync" if deployed == declared else "DRIFT"
152
+ if state == "in sync":
153
+ typer.secho("status : in sync", fg=typer.colors.GREEN)
154
+ return
155
+ typer.secho(
156
+ f"status : {state} — the services will read {deployed or 'nothing'} "
157
+ f"while the CDK config declares {declared}. Deploy with "
158
+ f"`g3dt dict deploy`, or update dictionaryVersion in the wrapper "
159
+ f"config and redeploy.",
160
+ fg=typer.colors.YELLOW,
161
+ )
162
+ if strict:
163
+ raise typer.Exit(1)
164
+
165
+
81
166
  @app.command()
82
167
  def upload(
83
168
  env: Optional[str] = typer.Option(None, "--env", "-e", help=ENV_OPT),
@@ -88,6 +173,13 @@ def upload(
88
173
  ) -> None:
89
174
  """Upload the (already pulled) dictionary JSON to the env's S3 location.
90
175
 
176
+ Stamps the object's S3 metadata `version` from the JSON's _settings, which
177
+ is what `g3dt dict status` reads back.
178
+
179
+ Examples:
180
+ g3dt dict upload --env staging
181
+ g3dt dict upload --env staging --version v1.1.7
182
+
91
183
  Targeting production requires typing the context/env name to confirm.
92
184
  """
93
185
  env = resolve.active_env(env)
@@ -116,17 +208,25 @@ def deploy(
116
208
  restart_services: Optional[str] = typer.Option(
117
209
  None, "--restart-services",
118
210
  help="Comma-separated deployment names restarted after the upload, in "
119
- "order; default: the env's SSM app/restart_services.",
211
+ "order; default: the env's SSM app/restart_services (the CDK "
212
+ "config's k8s.schemaRestartServices).",
213
+ ),
214
+ sync: bool = typer.Option(
215
+ False, "--sync",
216
+ help="Run 'argocd app sync' on the commons app before the restart "
217
+ "(off by default; add it when the app is behind the merged revision).",
120
218
  ),
121
219
  ) -> None:
122
220
  """Pull + upload the dictionary and restart Gen3 schema microservices.
123
221
 
124
222
  Wraps services/dictionary/deploy_dd.sh. Requires an interactive ArgoCD SSO
125
- login, so it runs locally only.
223
+ login, so it runs locally only. The restart never syncs the ArgoCD app
224
+ unless --sync is given.
126
225
 
127
226
  The version defaults to the env's `dictionary_version`, a CDK INPUT: edit
128
- config/<project>.<env>.json in gen3-aws-data-pipeline and `cdk deploy` to
129
- change what an env declares. Pass --version to deploy a different tag now
227
+ config/<project>.<env>.json in your deployment wrapper (the repo that pins
228
+ the aws-gen3-pipeline template) and `cdk deploy` to change what an env
229
+ declares. Pass --version to deploy a different tag now
130
230
  without that round trip — which is how one dictionary gets promoted across
131
231
  environments. `g3dt config diff` reports the resulting drift until the CDK
132
232
  config catches up.
@@ -146,6 +246,8 @@ def deploy(
146
246
  env_vars = script_env(e, _version(e, version))
147
247
  if restart_services:
148
248
  env_vars["G3DT_RESTART_SERVICES"] = restart_services
249
+ if sync:
250
+ env_vars["G3DT_SYNC"] = "1"
149
251
  runner.run(
150
252
  runner.bash_script("services/dictionary/deploy_dd.sh", env),
151
253
  env=env_vars,
@@ -35,7 +35,9 @@ def register(
35
35
  env: Optional[str] = typer.Option(None, "--env", "-e", help=ENV_OPT),
36
36
  dry_run: bool = typer.Option(
37
37
  False, "--dry-run", "-d",
38
- help="Scan + write file_metadata only; skip indexd."
38
+ help="Scan the prefixes and record what was found in the warehouse's "
39
+ "file_metadata table, but register NOTHING with indexd. Note this "
40
+ "still writes that table (unlike --dry-run elsewhere in g3dt)."
39
41
  ),
40
42
  force: bool = typer.Option(
41
43
  False, "--force",
@@ -7,7 +7,15 @@ import typer
7
7
 
8
8
  from g3dt.cli._internal import dispatch, registry, resolve
9
9
 
10
- app = typer.Typer(no_args_is_help=True, help="Track EC2-dispatched jobs.")
10
+ app = typer.Typer(
11
+ no_args_is_help=True,
12
+ help="Track EC2-dispatched jobs.\n\n"
13
+ "Every `--on ec2` dispatch (metadata upload / upload-all, delete "
14
+ "metadata, indexd register) prints a run id and records it in this "
15
+ "laptop's ~/.g3dt/runs.json; these commands read that file and the "
16
+ "run's SSM Run Command status / CloudWatch logs. No --env: the run id "
17
+ "already names its environment.",
18
+ )
11
19
 
12
20
 
13
21
  def _status_color(state: str) -> str:
@@ -79,9 +87,21 @@ def stop(run_id: str = typer.Argument(..., help="Run id from dispatch.")) -> Non
79
87
 
80
88
  @app.command()
81
89
  def logs(
82
- run_id: str = typer.Argument(..., help="Run id from dispatch."),
83
- follow: bool = typer.Option(False, "--follow", "-f", help="Stream new output as it arrives."),
90
+ run_id: str = typer.Argument(
91
+ ..., help="Run id printed by the --on ec2 dispatch (see `g3dt jobs list`)."
92
+ ),
93
+ follow: bool = typer.Option(
94
+ False, "--follow", "-f",
95
+ help="Stream new output as it arrives, until the run ends.",
96
+ ),
84
97
  ) -> None:
85
- """Print (and optionally follow) the CloudWatch logs for a dispatched run."""
98
+ """Print (and optionally follow) the CloudWatch logs for a dispatched run.
99
+
100
+ Empty output in the first minute is normal: the log stream is created
101
+ when the box starts writing.
102
+
103
+ Examples:
104
+ g3dt jobs logs 20260908T093000-metadata-upload --follow
105
+ """
86
106
  resolve.announce_context()
87
107
  dispatch.logs(run_id, follow=follow)
@@ -10,6 +10,10 @@ comma-separated list, restarted in order) and ``app/etl_cronjob``;
10
10
  ``--restart-services`` / ``--etl-cronjob`` override them for one run, and
11
11
  environments deployed without the block keep the classic Gen3 set
12
12
  (sheepdog, peregrine, guppy, portal / etl-cronjob).
13
+
14
+ None of these commands runs ``argocd app sync`` unless ``--sync`` is passed
15
+ (5.0.0; before that ``restart-ms`` always synced first). The flag reaches the
16
+ wrapped scripts as ``G3DT_SYNC=1``.
13
17
  """
14
18
  from __future__ import annotations
15
19
 
@@ -22,7 +26,16 @@ from g3dt.cli._internal import resolve, runner, safety
22
26
  from g3dt.cli._internal.resolve import env_of
23
27
  from g3dt.cli._internal.helptext import ENV_OPT
24
28
 
25
- app = typer.Typer(no_args_is_help=True, help="ArgoCD / Kubernetes restarts (local).")
29
+ app = typer.Typer(
30
+ no_args_is_help=True,
31
+ help="Restart the commons' Gen3 services / run its ETL via ArgoCD "
32
+ "(laptop only: needs the browser SSO login).",
33
+ )
34
+
35
+ LAPTOP_NOTE = (
36
+ "Requires an interactive ArgoCD SSO login (a browser opens), so it runs "
37
+ "on your laptop only — never on EC2."
38
+ )
26
39
 
27
40
  _SCHEMA = "services/k8s_ops/argocd_restart_schema.sh"
28
41
  _ETL = "services/k8s_ops/argocd_restart_etl.sh"
@@ -36,29 +49,50 @@ _ETL_CRONJOB_HELP = (
36
49
  "ETL cronjob name; default: the env's SSM app/etl_cronjob "
37
50
  "(the CDK config's k8s.etlCronjob)."
38
51
  )
52
+ SYNC_HELP = (
53
+ "Run 'argocd app sync' on the commons app before restarting (off by "
54
+ "default since 5.0.0; add it when the app is behind the merged revision)."
55
+ )
39
56
 
40
57
 
41
58
  def restart_env(e, restart_services: Optional[str] = None,
42
- etl_cronjob: Optional[str] = None) -> dict:
43
- """script_env plus per-run restart-target overrides (flags beat SSM)."""
59
+ etl_cronjob: Optional[str] = None,
60
+ sync: bool = False) -> dict:
61
+ """script_env plus per-run restart overrides (flags beat SSM).
62
+
63
+ ``sync`` exports ``G3DT_SYNC=1``, which the wrapper scripts translate into
64
+ the ``-s`` flag of exactly one argocd_restart_*.sh call.
65
+ """
44
66
  env_vars = script_env(e)
45
67
  if restart_services:
46
68
  env_vars["G3DT_RESTART_SERVICES"] = restart_services
47
69
  if etl_cronjob:
48
70
  env_vars["G3DT_ETL_CRONJOB"] = etl_cronjob
71
+ if sync:
72
+ env_vars["G3DT_SYNC"] = "1"
49
73
  return env_vars
50
74
 
51
75
 
52
76
  @app.command(name="restart-schema")
53
77
  def restart_schema(
54
78
  env: Optional[str] = typer.Option(None, "--env", "-e", help=ENV_OPT),
55
- sync: bool = typer.Option(False, "--sync", help="argocd app sync first."),
79
+ sync: bool = typer.Option(False, "--sync", help=SYNC_HELP),
56
80
  restart_services: Optional[str] = typer.Option(
57
81
  None, "--restart-services", help=_RESTART_SERVICES_HELP
58
82
  ),
59
83
  ) -> None:
60
84
  """Restart the schema microservices, in the env's configured order.
61
85
 
86
+ Each deployment is restarted in turn and polled until ArgoCD reports it
87
+ Healthy before the next starts. Use it after a dictionary upload (the
88
+ services re-read the schema at startup) or to roll guppy/portal after an
89
+ ETL. Requires an interactive ArgoCD SSO login (a browser opens), so it
90
+ runs on your laptop only — never on EC2.
91
+
92
+ Examples:
93
+ g3dt k8s restart-schema --env staging
94
+ g3dt k8s restart-schema --env staging --restart-services guppy-deployment,portal-deployment
95
+
62
96
  Targeting production requires typing the context/env name to confirm.
63
97
  """
64
98
  env = resolve.active_env(env)
@@ -69,19 +103,29 @@ def restart_schema(
69
103
  args.append("-s")
70
104
  runner.run(
71
105
  runner.bash_script(_SCHEMA, *args),
72
- env=restart_env(e, restart_services=restart_services),
106
+ env=restart_env(e, restart_services=restart_services, sync=sync),
73
107
  )
74
108
 
75
109
 
76
110
  @app.command(name="restart-etl")
77
111
  def restart_etl(
78
112
  env: Optional[str] = typer.Option(None, "--env", "-e", help=ENV_OPT),
79
- sync: bool = typer.Option(False, "--sync", help="argocd app sync first."),
113
+ sync: bool = typer.Option(False, "--sync", help=SYNC_HELP),
80
114
  etl_cronjob: Optional[str] = typer.Option(
81
115
  None, "--etl-cronjob", help=_ETL_CRONJOB_HELP
82
116
  ),
83
117
  ) -> None:
84
- """Create + run the ETL cronjob and wait for completion.
118
+ """Run the ETL (create a Job from the cronjob) and wait for completion.
119
+
120
+ Refreshes your kubeconfig, creates a Job from the env's ETL cronjob, polls
121
+ it, then greps the tube container log for "Exit code: 0" — the pod can
122
+ report Failed and still have succeeded. Run it after a metadata upload
123
+ so the portal shows the new data. Requires an interactive ArgoCD SSO
124
+ login (a browser opens), so it runs on your laptop only — never on EC2.
125
+
126
+ Examples:
127
+ g3dt k8s restart-etl --env staging
128
+ g3dt k8s restart-etl --env staging --sync # the app is behind the merged revision
85
129
 
86
130
  Targeting production requires typing the context/env name to confirm.
87
131
  """
@@ -93,13 +137,14 @@ def restart_etl(
93
137
  args.append("-s")
94
138
  runner.run(
95
139
  runner.bash_script(_ETL, *args),
96
- env=restart_env(e, etl_cronjob=etl_cronjob),
140
+ env=restart_env(e, etl_cronjob=etl_cronjob, sync=sync),
97
141
  )
98
142
 
99
143
 
100
144
  @app.command(name="restart-ms")
101
145
  def restart_ms(
102
146
  env: Optional[str] = typer.Option(None, "--env", "-e", help=ENV_OPT),
147
+ sync: bool = typer.Option(False, "--sync", help=SYNC_HELP),
103
148
  restart_services: Optional[str] = typer.Option(
104
149
  None, "--restart-services", help=_RESTART_SERVICES_HELP
105
150
  ),
@@ -107,7 +152,17 @@ def restart_ms(
107
152
  None, "--etl-cronjob", help=_ETL_CRONJOB_HELP
108
153
  ),
109
154
  ) -> None:
110
- """Restart both ETL and schema microservices (wraps restart_etl_and_ms.sh).
155
+ """Run the ETL, then restart the schema microservices (restart_etl_and_ms.sh).
156
+
157
+ The ETL runs first (--etl-cronjob), then the services restart serially in
158
+ the configured order (--restart-services). Nothing is synced in ArgoCD
159
+ unless --sync is given. Requires an interactive ArgoCD SSO login (a
160
+ browser opens, twice — once per wrapped step), so it runs on your laptop
161
+ only — never on EC2.
162
+
163
+ Examples:
164
+ g3dt k8s restart-ms --env staging
165
+ g3dt k8s restart-ms --env staging --restart-services guppy-deployment
111
166
 
112
167
  Targeting production requires typing the context/env name to confirm —
113
168
  this restarts every Gen3 microservice in the target commons.
@@ -117,5 +172,8 @@ def restart_ms(
117
172
  safety.confirm_prod_strict("kubernetes full restart", env)
118
173
  runner.run(
119
174
  runner.bash_script(_ETL_AND_MS, env),
120
- env=restart_env(e, restart_services=restart_services, etl_cronjob=etl_cronjob),
175
+ env=restart_env(
176
+ e, restart_services=restart_services, etl_cronjob=etl_cronjob,
177
+ sync=sync,
178
+ ),
121
179
  )