Skip to content

Commit dd94acf

Browse files
committed
Add simple DayOA catalog smoke command
1 parent 16bb983 commit dd94acf

9 files changed

Lines changed: 281 additions & 17 deletions

config/daylily_available_repositories.yaml

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,34 @@ repositories:
102102
default_ref: 2.0.19
103103
relative_path: daylily-omics-analysis
104104
analysis_commands:
105+
- command_id: simple-test
106+
display_name: "Simple Test"
107+
description: "Smoke-test DayOA shell initialization and local help target without staged sample inputs."
108+
datasource: DayOA
109+
launcher: workflow_launch
110+
command_class: utility
111+
input_contract: none
112+
requires_staging: false
113+
requires_run_mount: false
114+
runtime_parameters: {}
115+
input_requirements: {}
116+
targets:
117+
- help
118+
genome: hg38
119+
jobs: 1
120+
aligners: []
121+
dedupers: []
122+
snv_callers: []
123+
sv_callers: []
124+
dy_command: "source dyoainit; dy-a local hg38; dy-r -p -k -j 1 help"
125+
dryrun_dy_command: "source dyoainit; dy-a local hg38; dy-r -p -k -j 1 help"
126+
compatible_platforms:
127+
- local
128+
compatible_data_modes:
129+
- none
130+
git_tag: 2.0.19
131+
no_containerized: true
132+
validation_runs: []
105133
- command_id: illumina_snv_alignstats
106134
display_name: "Illumina SNV + Alignstats"
107135
description: "Illumina WGS Sentieon DNAscope and alignment-statistics launch profile."

daylily_ec/cli.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2380,6 +2380,16 @@ def workflow_launch(
23802380
"--stage-base",
23812381
help="Base staging directory to scan when --stage-dir is omitted.",
23822382
),
2383+
input_staging: bool = typer.Option(
2384+
True,
2385+
"--input-staging/--no-input-staging",
2386+
help="Copy staged samples/units into the workflow clone.",
2387+
),
2388+
default_activation: bool = typer.Option(
2389+
True,
2390+
"--default-activation/--no-default-activation",
2391+
help="Run the standard dyoainit plus Slurm day_activate setup before --dy-command.",
2392+
),
23832393
session_name: Optional[str] = typer.Option(
23842394
None,
23852395
"--session-name",
@@ -2566,6 +2576,10 @@ def workflow_launch(
25662576
):
25672577
if value is not None:
25682578
argv.extend([flag, value])
2579+
if not input_staging:
2580+
argv.append("--no-input-staging")
2581+
if not default_activation:
2582+
argv.append("--no-default-activation")
25692583
argv.append("--skip-project-check" if skip_project_check else "--strict-project-check")
25702584
if no_containerized:
25712585
argv.append("--no-containerized")

daylily_ec/repositories.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515

1616
CATALOG_VERSION = 2
1717
SUPPORTED_CATALOG_VERSIONS = {1, CATALOG_VERSION}
18-
COMMAND_CLASSES = {"sample_analysis", "run_analysis"}
18+
COMMAND_CLASSES = {"sample_analysis", "run_analysis", "utility"}
1919
INPUT_CONTRACTS = {"sample_manifest", "run_context", "none"}
2020
EXPORT_TRIGGERS = {"none", "on-success", "on-fail", "all"}
2121
VALIDATION_STATUSES = {"success", "failed", "blocked", "not_run"}
@@ -363,6 +363,13 @@ def _validate_launcher(self) -> "AnalysisCommand":
363363
raise ValueError("run_analysis commands must not require sample staging")
364364
if not self.requires_run_mount:
365365
raise ValueError("run_analysis commands must require run mounts")
366+
if self.command_class == "utility":
367+
if self.input_contract != "none":
368+
raise ValueError("utility commands must use none input")
369+
if self.requires_staging:
370+
raise ValueError("utility commands must not require sample staging")
371+
if self.requires_run_mount:
372+
raise ValueError("utility commands must not require run mounts")
366373
if not self.compatible_platforms:
367374
raise ValueError("compatible_platforms must not be empty")
368375
if not self.compatible_data_modes:
@@ -500,6 +507,8 @@ def launch_argv(
500507
argv.append("--skip-project-check" if skip_project_check else "--strict-project-check")
501508
if self.no_containerized:
502509
argv.append("--no-containerized")
510+
if self.input_contract == "none":
511+
argv.extend(["--no-input-staging", "--no-default-activation"])
503512
if export_destination_s3_uri:
504513
argv.extend(["--export-destination-s3-uri", export_destination_s3_uri])
505514
if export_trigger != "none":

daylily_ec/resources/payload/config/daylily_available_repositories.yaml

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,34 @@ repositories:
102102
default_ref: 2.0.19
103103
relative_path: daylily-omics-analysis
104104
analysis_commands:
105+
- command_id: simple-test
106+
display_name: "Simple Test"
107+
description: "Smoke-test DayOA shell initialization and local help target without staged sample inputs."
108+
datasource: DayOA
109+
launcher: workflow_launch
110+
command_class: utility
111+
input_contract: none
112+
requires_staging: false
113+
requires_run_mount: false
114+
runtime_parameters: {}
115+
input_requirements: {}
116+
targets:
117+
- help
118+
genome: hg38
119+
jobs: 1
120+
aligners: []
121+
dedupers: []
122+
snv_callers: []
123+
sv_callers: []
124+
dy_command: "source dyoainit; dy-a local hg38; dy-r -p -k -j 1 help"
125+
dryrun_dy_command: "source dyoainit; dy-a local hg38; dy-r -p -k -j 1 help"
126+
compatible_platforms:
127+
- local
128+
compatible_data_modes:
129+
- none
130+
git_tag: 2.0.19
131+
no_containerized: true
132+
validation_runs: []
105133
- command_id: illumina_snv_alignstats
106134
display_name: "Illumina SNV + Alignstats"
107135
description: "Illumina WGS Sentieon DNAscope and alignment-statistics launch profile."

daylily_ec/scripts/daylily_run_omics_analysis_headnode.py

Lines changed: 41 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,18 @@ def build_parser() -> argparse.ArgumentParser:
214214
default="/fsx/staging/staged_external_sequencing_data",
215215
help="Base staging directory to scan when --stage-dir is omitted",
216216
)
217+
parser.add_argument(
218+
"--no-input-staging",
219+
dest="input_staging",
220+
action="store_false",
221+
help="Do not copy staged samples/units or write a run context before launching",
222+
)
223+
parser.add_argument(
224+
"--no-default-activation",
225+
dest="default_activation",
226+
action="store_false",
227+
help="Do not run the standard dyoainit plus Slurm activation before --dy-command",
228+
)
217229
parser.add_argument(
218230
"--session-name",
219231
help="Name of the tmux session to create on the head node. Defaults to --analysis-id.",
@@ -318,7 +330,7 @@ def build_parser() -> argparse.ArgumentParser:
318330
help="Ursa analysis EUID linked to the exported analysis directory external object",
319331
)
320332
parser.add_argument("--dry-run", action="store_true")
321-
parser.set_defaults(skip_project_check=True)
333+
parser.set_defaults(skip_project_check=True, input_staging=True, default_activation=True)
322334
return parser
323335

324336

@@ -408,21 +420,27 @@ def main(argv: Optional[List[str]] = None) -> int:
408420

409421
run_context_content: Optional[str] = None
410422
if args.run_context_file:
423+
if not args.input_staging:
424+
raise CommandError("--run-context-file cannot be used with --no-input-staging.")
411425
if args.stage_dir:
412426
raise CommandError("--stage-dir cannot be used with --run-context-file.")
413427
run_context_path = Path(args.run_context_file).expanduser()
414428
if not run_context_path.is_file():
415429
raise CommandError(f"Run context file not found: {run_context_path}")
416430
run_context_content = run_context_path.read_text(encoding="utf-8")
417431
stage_config = None
418-
else:
432+
elif args.input_staging:
419433
stage_config = discover_stage_config(
420434
target.instance_id,
421435
args.profile,
422436
region,
423437
args.stage_dir,
424438
args.stage_base,
425439
)
440+
else:
441+
if args.stage_dir:
442+
raise CommandError("--stage-dir cannot be used with --no-input-staging.")
443+
stage_config = None
426444

427445
if args.dy_command:
428446
dy_command = args.dy_command
@@ -446,6 +464,8 @@ def main(argv: Optional[List[str]] = None) -> int:
446464
skip_check = "true" if args.skip_project_check else "false"
447465
run_context_mode = run_context_content is not None
448466
run_context_mode_literal = "true" if run_context_mode else "false"
467+
input_staging_mode_literal = "true" if args.input_staging else "false"
468+
default_activation_literal = "true" if args.default_activation else "false"
449469
run_context_payload = shlex.quote(run_context_content or "")
450470
export_destination_literal = shlex.quote(args.export_destination_s3_uri or "")
451471
delete_on_export_success = "true" if args.delete_on_export_success else "false"
@@ -483,6 +503,8 @@ def main(argv: Optional[List[str]] = None) -> int:
483503
ANALYSIS_ID={shlex.quote(analysis_id)}
484504
EXECUTING_ENTITY={shlex.quote(executing_entity)}
485505
RUN_CONTEXT_MODE={run_context_mode_literal}
506+
INPUT_STAGING_MODE={input_staging_mode_literal}
507+
DEFAULT_ACTIVATION={default_activation_literal}
486508
RUN_CONTEXT_PAYLOAD={run_context_payload}
487509
STAGE_SAMPLES={shlex.quote(stage_samples_path)}
488510
STAGE_UNITS={shlex.quote(stage_units_path)}
@@ -529,9 +551,11 @@ def main(argv: Optional[List[str]] = None) -> int:
529551
mkdir -p config
530552
if [[ "$RUN_CONTEXT_MODE" == "true" ]]; then
531553
printf '%s' "$RUN_CONTEXT_PAYLOAD" > config/runs.tsv
532-
else
554+
elif [[ "$INPUT_STAGING_MODE" == "true" ]]; then
533555
cp "$STAGE_SAMPLES" config/samples.tsv
534556
cp "$STAGE_UNITS" config/units.tsv
557+
else
558+
echo "[INFO] Input staging skipped for this catalog command."
535559
fi
536560
537561
if [[ ! -f "$HOME/miniconda3/etc/profile.d/conda.sh" ]]; then
@@ -550,18 +574,20 @@ def main(argv: Optional[List[str]] = None) -> int:
550574
if [[ "$SKIP_PROJECT_CHECK" == "true" ]]; then
551575
dyoa_args+=(--skip-project-check)
552576
fi
553-
set +u
554-
. dyoainit "${{dyoa_args[@]}}"
555-
set -u
556-
set +e
557-
set +u
558-
. bin/day_activate slurm {shlex.quote(args.genome)} remote
559-
activate_status=$?
560-
set -u
561-
set -e
562-
if [[ "$activate_status" != "0" ]]; then
563-
echo "[ERROR] day_activate failed with status $activate_status"
564-
exit "$activate_status"
577+
if [[ "$DEFAULT_ACTIVATION" == "true" ]]; then
578+
set +u
579+
. dyoainit "${{dyoa_args[@]}}"
580+
set -u
581+
set +e
582+
set +u
583+
. bin/day_activate slurm {shlex.quote(args.genome)} remote
584+
activate_status=$?
585+
set -u
586+
set -e
587+
if [[ "$activate_status" != "0" ]]; then
588+
echo "[ERROR] day_activate failed with status $activate_status"
589+
exit "$activate_status"
590+
fi
565591
fi
566592
set +e
567593
eval "$DY_COMMAND"
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# DayOA To DYEC Release Train Ledger
2+
3+
## Objective
4+
Close the remaining boot-script publication gap, release the current DayOA work, release the current DYEC utility-command work, update DYEC DayOA pins to the new DayOA release, and publish a final DYEC release tag.
5+
6+
## Gate 0 Inventory
7+
- DYEC repo: `/Users/jmajor/.codex/worktrees/dyec-fsx-dra-mounts/daylily-ephemeral-cluster`
8+
- DYEC branch: `codex/dyec-dewey-registration-refactor-20260528`
9+
- DYEC initial state: clean relative to origin except current uncommitted `simple-test` catalog/launcher changes.
10+
- DYEC latest fetched non-v semver tag before this train: `5.0.29`
11+
- Planned DYEC utility release tag: `5.0.30`
12+
- Planned DYEC pin-update release tag: `5.0.31`
13+
- DayOA repo: `/Users/jmajor/projects/daylily/daylily-omics-analysis`
14+
- DayOA branch: `codex/dayoa-local-evidence-dewey-refactor-20260528`
15+
- DayOA latest fetched non-v semver tag before this train: `2.0.20`
16+
- Intermediate DayOA release tags created in this train: `2.0.21`, `2.0.22`
17+
- Final DayOA pin target release tag: `2.0.23`
18+
- Remaining prior blocker: public Daylily boot-script object backup/upload failed with `AccessDenied` using profile `lsmc`.
19+
20+
## Ledger
21+
| ID | Lane | Task | Status | Evidence | Blocker | Notes |
22+
| --- | --- | --- | --- | --- | --- | --- |
23+
| G0-001 | Inventory | Record repo state, tag baselines, and remaining publication blocker. | SUCCESS | Gate 0 inventory above. | | This ledger coordinates DayOA and DYEC release order. |
24+
| PUB-001 | S3 publish | Back up and update the remaining public Daylily boot-script target, then verify readback SHA. | SUCCESS | `AWS_PROFILE=daylily` copied the current object to `s3://daylily-dayoa-references-usw2/runtime_assets/cluster_boot_config/backups/post_install_ubuntu_combined.sh.pre-overlay-removal-20260529T022223Z`, uploaded the local script, and read back SHA `4b4363ec4e123872c76498c2a5d42b91ce8c43ab376147ddd20865f0911e6a3a`, matching local. | | No S3 deletion was performed. |
25+
| DAYOA-001 | DayOA release | Validate, commit, annotated-tag the DayOA release, verify, push branch and tag. | SUCCESS | DayOA focused validation passed. Tags `2.0.21`, `2.0.22`, and final pin target `2.0.23` were created as annotated tags and pushed. Final tag `2.0.23` peels to commit `3d5e86c38f1a03a2a47cbad570d4ace5386bbec6`. | | Earlier pushed tags are preserved; DYEC pins `2.0.23`. |
26+
| DYEC-001 | DYEC utility release | Validate, commit current `simple-test` catalog/launcher changes, annotated-tag `5.0.30`, verify, push branch and tag. | PENDING | | | Keeps current dirty work as its own release. |
27+
| DYEC-002 | DYEC pin update | Update DYEC DayOA pins to `2.0.21`, validate, commit, annotated-tag `5.0.31`, verify, push branch and tag. | PENDING | | | Final DYEC tag should point to the new DayOA release. |
28+
| FINAL-001 | Final report | Report all release tags, pushed refs, validation, and any remaining blockers. | PENDING | | | Report exact tag objects and peeled commits. |

tests/test_cli_registry_v2.py

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1811,6 +1811,48 @@ def fake_launch(argv: list[str]) -> int:
18111811
assert "--stage-dir" not in argv
18121812

18131813

1814+
def test_workflow_launch_forwards_no_input_utility_flags(monkeypatch) -> None:
1815+
import daylily_ec.scripts.daylily_run_omics_analysis_headnode as launch_module
1816+
1817+
calls: dict[str, object] = {}
1818+
_activate_dayec_runtime(monkeypatch)
1819+
1820+
def fake_launch(argv: list[str]) -> int:
1821+
calls["argv"] = argv
1822+
return 0
1823+
1824+
monkeypatch.setattr(launch_module, "main", fake_launch)
1825+
1826+
result = runner.invoke(
1827+
app,
1828+
[
1829+
"workflow",
1830+
"launch",
1831+
"--profile",
1832+
"dev",
1833+
"--region",
1834+
"us-west-2",
1835+
"--cluster",
1836+
"cluster-a",
1837+
"--analysis-id",
1838+
"simple-test",
1839+
"--executing-entity",
1840+
"johnm",
1841+
"--dy-command",
1842+
"source dyoainit; dy-a local hg38; dy-r -p -k -j 1 help",
1843+
"--no-input-staging",
1844+
"--no-default-activation",
1845+
],
1846+
)
1847+
1848+
assert result.exit_code == 0
1849+
argv = calls["argv"]
1850+
assert "--no-input-staging" in argv
1851+
assert "--no-default-activation" in argv
1852+
assert "--stage-dir" not in argv
1853+
assert "--run-context-file" not in argv
1854+
1855+
18141856
def test_workflow_launch_requires_analysis_identity(monkeypatch) -> None:
18151857
_activate_dayec_runtime(monkeypatch)
18161858

tests/test_repository_catalog.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
/ "daylily_available_repositories.yaml"
2525
)
2626
UNVALIDATED_COMMAND_IDS = {
27+
"simple-test",
2728
"illumina_run_qc_bclconvert",
2829
"ultima_snv_alignstats_kitchensink",
2930
"ont_snv_alignstats_kitchensink",
@@ -204,6 +205,7 @@ def test_repository_catalog_commands_have_run_metadata() -> None:
204205

205206
command_ids = {command.command_id for command in catalog.commands()}
206207
assert {
208+
"simple-test",
207209
"illumina_snv_alignstats",
208210
"illumina_snv_alignstats_relatedness_vep_multiqc",
209211
"illumina_hg002_kitchensink_multiqc",
@@ -452,6 +454,27 @@ def test_repository_catalog_commands_have_run_metadata() -> None:
452454
assert " -j 125 -p -k" in inflection_bjuice.dy_command
453455
assert inflection_bjuice.dryrun_dy_command.endswith(" -n")
454456

457+
simple_test = catalog.get_command("simple-test")
458+
assert simple_test.command_class == "utility"
459+
assert simple_test.input_contract == "none"
460+
assert simple_test.requires_staging is False
461+
assert simple_test.requires_run_mount is False
462+
assert simple_test.targets == ["help"]
463+
assert simple_test.genome == "hg38"
464+
assert simple_test.jobs == 1
465+
assert simple_test.dy_command == "source dyoainit; dy-a local hg38; dy-r -p -k -j 1 help"
466+
assert simple_test.dryrun_dy_command == simple_test.dy_command
467+
simple_launch_argv = simple_test.launch_argv(
468+
analysis_id="simple-test",
469+
executing_entity="johnm",
470+
)
471+
assert "--dy-command" in simple_launch_argv
472+
assert simple_test.dy_command in simple_launch_argv
473+
assert "--no-input-staging" in simple_launch_argv
474+
assert "--no-default-activation" in simple_launch_argv
475+
assert "--stage-dir" not in simple_launch_argv
476+
assert "--run-context-file" not in simple_launch_argv
477+
455478

456479
def test_repository_catalog_run_analysis_commands_require_run_context() -> None:
457480
catalog = load_repository_catalog(CATALOG_PATH)

0 commit comments

Comments
 (0)