From 29cf4830246c7f3714a5174067cfe2fbe9aa4e86 Mon Sep 17 00:00:00 2001 From: kazmosahebi Date: Tue, 9 Jun 2026 23:28:28 +1000 Subject: [PATCH] fix: scaffold submodules input in consumer ci.yml init only emitted tag/from-head/bump into the generated ci.yml, so the submodules input added in #40 never reached consumers -- and a hand-added line was clobbered on the next init --force. - init reads a 'submodules:' field from .hyperi-ci.yaml and renders it as the submodules input in the consumer ci.yml with: block (mirrors the publish-target branch), round-tripping it into the regenerated .hyperi-ci.yaml so re-init preserves it. - Runtime-safe: CIConfig is .get()-based, so the field is ignored by the config loader -- init-only. - docs/LESSONS.md: point the public-submodule path at the .hyperi-ci.yaml field + init render. Refs #39 --- docs/LESSONS.md | 5 +++-- src/hyperi_ci/init.py | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 2 deletions(-) diff --git a/docs/LESSONS.md b/docs/LESSONS.md index a52897e..99b38b3 100644 --- a/docs/LESSONS.md +++ b/docs/LESSONS.md @@ -300,8 +300,9 @@ build to ensure fresh artifacts, also via `hyperi-ci run build`. - Coverage: separate `.coverage.` files, combine at end - No test directory: exit 0 (graceful skip) - Submodule-dependent tests: - - Public submodule: set the `submodules` input on the reusable workflow - (e.g. `submodules: schemas`) so CI checks it out and the tests run. + - Public submodule: declare `submodules: schemas` in `.hyperi-ci.yaml`; + `hyperi-ci init` renders it as the reusable-workflow `submodules` input + in `ci.yml`, and the test job checks it out so the tests run. - Private submodule (e.g. `dfe-schemas` while private): GITHUB_TOKEN can't clone it, so mark dependent tests with `@pytest.mark.skipif(not schemas_dir.exists(), reason="submodule not checked out")` diff --git a/src/hyperi_ci/init.py b/src/hyperi_ci/init.py index 768f4e5..e121721 100644 --- a/src/hyperi_ci/init.py +++ b/src/hyperi_ci/init.py @@ -112,6 +112,26 @@ def detect_license(project_dir: Path) -> str: return _DEFAULT_LICENSE +def _detect_submodules(project_dir: Path) -> str: + """Read the ``submodules`` field from ``.hyperi-ci.yaml`` (else ""). + + Names submodule paths the CI test job should init (space-separated), + e.g. ``schemas``. Threaded into both the generated ci.yml (the + reusable-workflow ``submodules`` input) and the regenerated + ``.hyperi-ci.yaml``, so the value survives re-init. + """ + ci_config = project_dir / ".hyperi-ci.yaml" + if ci_config.exists(): + try: + data = yaml.safe_load(ci_config.read_text()) or {} + declared = data.get("submodules") if isinstance(data, dict) else None + if isinstance(declared, str) and declared.strip(): + return declared.strip() + except (OSError, UnicodeDecodeError, yaml.YAMLError): + pass + return "" + + def _license_header_text(license_id: str) -> str: """Return the license line for file headers.""" if license_id == "BUSL-1.1": @@ -169,6 +189,7 @@ def _render_hyperi_ci_yaml( project_name: str, project_dir: Path, license_id: str = _DEFAULT_LICENSE, + submodules: str = "", ) -> str: """Render .hyperi-ci.yaml with language-specific defaults.""" config: dict = { @@ -219,6 +240,11 @@ def _render_hyperi_ci_yaml( config["test"]["coverage"] = True config["typescript"] = {"package_manager": "auto"} + # Submodule paths the CI test job inits (space-separated, e.g. "schemas"). + # Emitted as the reusable-workflow `submodules` input in ci.yml. + if submodules: + config["submodules"] = submodules + header = ( f"# Project: {project_name}\n" "# File: .hyperi-ci.yaml\n" @@ -269,6 +295,7 @@ def _render_workflow( workflow_file: str, license_id: str = _DEFAULT_LICENSE, publish_target: str = "internal", + submodules: str = "", ) -> str: """Render consumer .github/workflows/ci.yml content.""" base = ( @@ -317,6 +344,9 @@ def _render_workflow( if publish_target != "internal": base += f" publish-target: {publish_target}\n" + if submodules: + base += f" submodules: {submodules}\n" + base += " secrets: inherit\n" return base @@ -564,6 +594,7 @@ def init_project( return 1 license_id = detect_license(project_dir) + submodules = _detect_submodules(project_dir) info(f"Initialising {project_name} as {detected} project (license: {license_id})") workflow_file = _LANGUAGE_WORKFLOW_MAP.get(detected) @@ -578,6 +609,7 @@ def init_project( project_name, project_dir, license_id=license_id, + submodules=submodules, ) config_path = project_dir / ".hyperi-ci.yaml" if _write_file(config_path, config_content, force=force): @@ -600,6 +632,7 @@ def init_project( project_name, workflow_file, license_id=license_id, + submodules=submodules, ) workflow_path = project_dir / ".github" / "workflows" / "ci.yml" if _write_file(