From c318dcb0f113cf61975119bf2eeacc1926c0ac3b Mon Sep 17 00:00:00 2001 From: Marlon Costa Date: Fri, 20 Feb 2026 12:52:47 -0300 Subject: [PATCH 01/34] chore(workspace): propagate unified make and release automation --- Makefile | 15 +- flexcore | 2 +- flext-api | 2 +- flext-auth | 2 +- flext-cli | 2 +- flext-core | 2 +- flext-db-oracle | 2 +- flext-dbt-ldap | 2 +- flext-dbt-ldif | 2 +- flext-dbt-oracle | 2 +- flext-dbt-oracle-wms | 2 +- flext-grpc | 2 +- flext-ldap | 2 +- flext-ldif | 2 +- flext-meltano | 2 +- flext-observability | 2 +- flext-oracle-oic | 2 +- flext-oracle-wms | 2 +- flext-plugin | 2 +- flext-quality | 2 +- flext-tap-ldap | 2 +- flext-tap-ldif | 2 +- flext-tap-oracle | 2 +- flext-tap-oracle-oic | 2 +- flext-tap-oracle-wms | 2 +- flext-target-ldap | 2 +- flext-target-ldif | 2 +- flext-target-oracle | 2 +- flext-target-oracle-oic | 2 +- flext-target-oracle-wms | 2 +- flext-web | 2 +- scripts/maintenance/enforce_python_version.py | 247 ++++++++++++++++++ scripts/release/build.py | 7 +- scripts/release/changelog.py | 5 +- scripts/release/notes.py | 9 +- scripts/release/run.py | 120 +++++---- scripts/release/shared.py | 20 +- scripts/release/version.py | 46 ++-- tests/scripts/release/test_release_scripts.py | 46 ++++ .../release/test_release_shared_and_run.py | 77 ++++++ 40 files changed, 536 insertions(+), 116 deletions(-) create mode 100644 scripts/maintenance/enforce_python_version.py create mode 100644 tests/scripts/release/test_release_scripts.py create mode 100644 tests/scripts/release/test_release_shared_and_run.py diff --git a/Makefile b/Makefile index 38f24acf..59f4040e 100644 --- a/Makefile +++ b/Makefile @@ -158,7 +158,7 @@ help: ## Show simple workspace verbs $(Q)echo " INTERACTIVE=1|0 Release prompt mode" $(Q)echo " DRY_RUN=1 Print plan, do not tag/push" $(Q)echo " PUSH=1 Push release commit/tag" - $(Q)echo " VERSION=0.10.0 TAG=v0.10.0 BUMP=patch Release controls" + $(Q)echo " VERSION= TAG=v BUMP=patch Release controls" $(Q)echo " CREATE_BRANCHES=1|0 Create release branches in workspace + projects" $(Q)echo " DEPS_REPORT=0 Skip dependency report after upgrade/typings" $(Q)echo "" @@ -171,7 +171,7 @@ help: ## Show simple workspace verbs $(Q)echo " make test PROJECT=flext-api PYTEST_ARGS=\"-k unit\" FAIL_FAST=1" $(Q)echo " make validate VALIDATE_SCOPE=workspace" $(Q)echo " make release BUMP=minor" - $(Q)echo " make release-ci VERSION=0.10.0 TAG=v0.10.0 RELEASE_PHASE=all" + $(Q)echo " make release-ci VERSION=0.11.0 TAG=v0.11.0 RELEASE_PHASE=all" $(Q)echo " NOTE: External projects (not in .gitmodules) require manual clone." setup: ## Install all projects into workspace .venv @@ -186,6 +186,7 @@ setup: ## Install all projects into workspace .venv $(Q)$(ENFORCE_WORKSPACE_VENV) $(Q)$(ENSURE_SELECTED_PROJECTS) $(Q)$(ENSURE_PROJECTS_EXIST) + $(Q)echo "Enforcing Python 3.13 version guards..."; python3.13 scripts/maintenance/enforce_python_version.py || exit 1 $(Q)$(AUTO_ADJUST_SELECTED_PROJECTS) $(Q)echo "Modernizing pyproject.toml files..."; \ $(POETRY_ENV) python scripts/dependencies/modernize_pyproject.py --skip-check 2>&1 | grep -E "^Phase|Total:|✓|No semantic" || true; \ @@ -284,6 +285,7 @@ upgrade: ## Upgrade Python dependencies to latest via Poetry $(Q)$(ENFORCE_WORKSPACE_VENV) $(Q)$(ENSURE_SELECTED_PROJECTS) $(Q)$(ENSURE_PROJECTS_EXIST) + $(Q)echo "Enforcing Python 3.13 version guards..."; python3.13 scripts/maintenance/enforce_python_version.py || exit 1 $(Q)echo "Modernizing pyproject.toml files..."; \ $(POETRY_ENV) python scripts/dependencies/modernize_pyproject.py --skip-check 2>&1 | grep -E "^Phase|Total:|✓|No semantic" || true; \ echo "" @@ -400,12 +402,16 @@ build: ## Build/package all selected projects $(Q)$(ORCHESTRATOR) --verb build $(if $(filter 1,$(FAIL_FAST)),--fail-fast) $(SELECTED_PROJECTS) release: ## Interactive workspace release orchestration + $(Q)$(ENSURE_NO_PROJECT_CONFLICT) $(Q)$(ENFORCE_WORKSPACE_VENV) + $(Q)$(ENSURE_SELECTED_PROJECTS) + $(Q)$(ENSURE_PROJECTS_EXIST) $(Q)python scripts/release/run.py \ --root "$(CURDIR)" \ --phase "$(RELEASE_PHASE)" \ --interactive "$(INTERACTIVE)" \ --create-branches "$(CREATE_BRANCHES)" \ + --projects $(SELECTED_PROJECTS) \ $(if $(DRY_RUN),--dry-run "$(DRY_RUN)",) \ $(if $(PUSH),--push "$(PUSH)",) \ $(if $(VERSION),--version "$(VERSION)",) \ @@ -413,12 +419,16 @@ release: ## Interactive workspace release orchestration $(if $(BUMP),--bump "$(BUMP)",) release-ci: ## Non-interactive release run for CI/tag workflows + $(Q)$(ENSURE_NO_PROJECT_CONFLICT) $(Q)$(ENFORCE_WORKSPACE_VENV) + $(Q)$(ENSURE_SELECTED_PROJECTS) + $(Q)$(ENSURE_PROJECTS_EXIST) $(Q)python scripts/release/run.py \ --root "$(CURDIR)" \ --phase "$(RELEASE_PHASE)" \ --interactive 0 \ --create-branches 0 \ + --projects $(SELECTED_PROJECTS) \ $(if $(DRY_RUN),--dry-run "$(DRY_RUN)",) \ $(if $(PUSH),--push "$(PUSH)",) \ $(if $(VERSION),--version "$(VERSION)",) \ @@ -470,6 +480,7 @@ ifeq ($(VALIDATE_SCOPE),workspace) $(Q)$(AUTO_ADJUST_SELECTED_PROJECTS) $(Q)mkdir -p .reports $(Q)echo "Running workspace validation (inventory + strict anti-drift gates)..." + $(Q)python3.13 scripts/maintenance/enforce_python_version.py --check || exit 1 $(Q)$(WORKSPACE_VENV)/bin/python scripts/core/generate_scripts_inventory.py --root . $(Q)$(WORKSPACE_VENV)/bin/python scripts/core/check_base_mk_sync.py $(Q)$(WORKSPACE_VENV)/bin/python scripts/github/lint_workflows.py --root . --report .reports/workflows/actionlint.json diff --git a/flexcore b/flexcore index 7b12d4b7..abf96234 160000 --- a/flexcore +++ b/flexcore @@ -1 +1 @@ -Subproject commit 7b12d4b7a062c04e8e4b803d50b19061e28b4cbb +Subproject commit abf96234a51ff6d45f033b5fd1d71d3aedcde7ca diff --git a/flext-api b/flext-api index 37d68b5d..391e5ced 160000 --- a/flext-api +++ b/flext-api @@ -1 +1 @@ -Subproject commit 37d68b5dee4fa1ea5aaf7c72b20b438e946f676c +Subproject commit 391e5ced5e7d302fac311aee183817094facd545 diff --git a/flext-auth b/flext-auth index 56660879..023e06d9 160000 --- a/flext-auth +++ b/flext-auth @@ -1 +1 @@ -Subproject commit 56660879174d07332ff06edde280864e1b7aa33e +Subproject commit 023e06d9c32789d8c82f4661f4c617ab18daf8b0 diff --git a/flext-cli b/flext-cli index 5e65b95d..e6240a04 160000 --- a/flext-cli +++ b/flext-cli @@ -1 +1 @@ -Subproject commit 5e65b95d16eff8dbf19a4655263622b982b61d54 +Subproject commit e6240a04c781ff31681db919ee8efdd4cd565209 diff --git a/flext-core b/flext-core index a474573b..281a4c0c 160000 --- a/flext-core +++ b/flext-core @@ -1 +1 @@ -Subproject commit a474573ba3e518e8e7d1590800ae07ca4e2779da +Subproject commit 281a4c0c93808155a09390501456ee9e09c4cd6b diff --git a/flext-db-oracle b/flext-db-oracle index 0c02a456..d059e818 160000 --- a/flext-db-oracle +++ b/flext-db-oracle @@ -1 +1 @@ -Subproject commit 0c02a456eeac940679251c934bd858410affb4b7 +Subproject commit d059e8180d6f6a110cdb0a252c1974fcecc91b42 diff --git a/flext-dbt-ldap b/flext-dbt-ldap index 6d5b62e7..3fd7465c 160000 --- a/flext-dbt-ldap +++ b/flext-dbt-ldap @@ -1 +1 @@ -Subproject commit 6d5b62e7c0fc01caacbc90c9ed242836c1c0485b +Subproject commit 3fd7465cd146e21a4149904c88c14af900db5414 diff --git a/flext-dbt-ldif b/flext-dbt-ldif index 5ab5a2cb..b00de7b5 160000 --- a/flext-dbt-ldif +++ b/flext-dbt-ldif @@ -1 +1 @@ -Subproject commit 5ab5a2cb40a26d9afbf1e2713b952c9053b847e7 +Subproject commit b00de7b59df70aa038d54e0d5bb3a58595f62b5a diff --git a/flext-dbt-oracle b/flext-dbt-oracle index d3617467..aa47e882 160000 --- a/flext-dbt-oracle +++ b/flext-dbt-oracle @@ -1 +1 @@ -Subproject commit d36174673293bf2748c8d681741523aa10e0aa6c +Subproject commit aa47e882952853c4ad0f035a9019da6e3ce68242 diff --git a/flext-dbt-oracle-wms b/flext-dbt-oracle-wms index dfa89830..cb1fad0e 160000 --- a/flext-dbt-oracle-wms +++ b/flext-dbt-oracle-wms @@ -1 +1 @@ -Subproject commit dfa89830ab65cb622a87b633a7d156a98fd4e964 +Subproject commit cb1fad0e4d9779872d784835ebddeb3d0fb7bdc1 diff --git a/flext-grpc b/flext-grpc index f9d5613e..a7a09fd6 160000 --- a/flext-grpc +++ b/flext-grpc @@ -1 +1 @@ -Subproject commit f9d5613e3dc6ef5969d935063fab9adc52d5399b +Subproject commit a7a09fd69fa8e1fa563a956e48e8100e8ccb7df2 diff --git a/flext-ldap b/flext-ldap index 36ceb892..cc25b4ae 160000 --- a/flext-ldap +++ b/flext-ldap @@ -1 +1 @@ -Subproject commit 36ceb892b457785465a8d390f067daf52a2baa00 +Subproject commit cc25b4aeb8a636a327dd4a3f81885c9d79ea817c diff --git a/flext-ldif b/flext-ldif index 291015f3..d15a7535 160000 --- a/flext-ldif +++ b/flext-ldif @@ -1 +1 @@ -Subproject commit 291015f3d02ef45cd36d0cb5b20130461d697b3f +Subproject commit d15a7535c742947852a6d5c9dbf1f1df82a0c015 diff --git a/flext-meltano b/flext-meltano index 0a3a4386..8377e2d8 160000 --- a/flext-meltano +++ b/flext-meltano @@ -1 +1 @@ -Subproject commit 0a3a4386d5e25d8a2b1f9896db76e3dc25dd2320 +Subproject commit 8377e2d89cc74334862b9e6507074d1c4b1c144f diff --git a/flext-observability b/flext-observability index af808de8..40fd24fa 160000 --- a/flext-observability +++ b/flext-observability @@ -1 +1 @@ -Subproject commit af808de828f7fbb29b3023ae122ec9b2122ec210 +Subproject commit 40fd24fa516513716c163d7f26f671e61bd3d6ed diff --git a/flext-oracle-oic b/flext-oracle-oic index 7e6286dd..e4997844 160000 --- a/flext-oracle-oic +++ b/flext-oracle-oic @@ -1 +1 @@ -Subproject commit 7e6286dd5f9b279e6fc2b6c8953ef722fc28137b +Subproject commit e4997844fcfa978de5fe180560de1d867983de7e diff --git a/flext-oracle-wms b/flext-oracle-wms index 3ef7d706..619dd476 160000 --- a/flext-oracle-wms +++ b/flext-oracle-wms @@ -1 +1 @@ -Subproject commit 3ef7d7065a8a5f3120caa114c5e0272ecaf1038c +Subproject commit 619dd47684f861f66428693182179e3239601cf1 diff --git a/flext-plugin b/flext-plugin index 9c405c1e..f43058df 160000 --- a/flext-plugin +++ b/flext-plugin @@ -1 +1 @@ -Subproject commit 9c405c1e7ff81c88bef1887057c91e4666f268bf +Subproject commit f43058df63d27981667413162813b5c574d81b36 diff --git a/flext-quality b/flext-quality index e942a599..d04cb537 160000 --- a/flext-quality +++ b/flext-quality @@ -1 +1 @@ -Subproject commit e942a599e1da20e37272f75f89504d8d1dc8557a +Subproject commit d04cb537b12869ef6bfc4d9e4edf2c5d55374599 diff --git a/flext-tap-ldap b/flext-tap-ldap index 7526648a..10fd918d 160000 --- a/flext-tap-ldap +++ b/flext-tap-ldap @@ -1 +1 @@ -Subproject commit 7526648a86c4ec48566a455b863263db8a5bfe3e +Subproject commit 10fd918d314354691ec72cae7b34c00e35cc4601 diff --git a/flext-tap-ldif b/flext-tap-ldif index 1e7855c4..c78472e7 160000 --- a/flext-tap-ldif +++ b/flext-tap-ldif @@ -1 +1 @@ -Subproject commit 1e7855c4ed4a2e7ecd3f1362883fc72837f35bd5 +Subproject commit c78472e7c08163df9a48c00bb0986e4ccf594097 diff --git a/flext-tap-oracle b/flext-tap-oracle index d6c52499..738703ac 160000 --- a/flext-tap-oracle +++ b/flext-tap-oracle @@ -1 +1 @@ -Subproject commit d6c5249938df7419db4c11e0b4a51cbe4de6de8d +Subproject commit 738703ac0d3c9ec86cbf74925007269f37a17464 diff --git a/flext-tap-oracle-oic b/flext-tap-oracle-oic index c9bc050e..d5762a59 160000 --- a/flext-tap-oracle-oic +++ b/flext-tap-oracle-oic @@ -1 +1 @@ -Subproject commit c9bc050ef90ad765362744826368b32b347f45f2 +Subproject commit d5762a59593e65ba1efd6d8ff4995b74ff3b9022 diff --git a/flext-tap-oracle-wms b/flext-tap-oracle-wms index 288b0374..cb0b0f1e 160000 --- a/flext-tap-oracle-wms +++ b/flext-tap-oracle-wms @@ -1 +1 @@ -Subproject commit 288b0374c7468b42ca926a715217bd1db0871d6b +Subproject commit cb0b0f1e0af7fc0a4945bd8f98de8648de31eaf3 diff --git a/flext-target-ldap b/flext-target-ldap index a65f13fb..84f265bd 160000 --- a/flext-target-ldap +++ b/flext-target-ldap @@ -1 +1 @@ -Subproject commit a65f13fb2e111d4cb977136ac10c62c71d4c169b +Subproject commit 84f265bd38be1a82c36fddfae66de22c039fbdf4 diff --git a/flext-target-ldif b/flext-target-ldif index a2e17650..19fd9334 160000 --- a/flext-target-ldif +++ b/flext-target-ldif @@ -1 +1 @@ -Subproject commit a2e176503df1321b3f3d4437a6febf6860d93ced +Subproject commit 19fd933481c80ce272243ff2739bfa8df08361c8 diff --git a/flext-target-oracle b/flext-target-oracle index 07228747..462c11a6 160000 --- a/flext-target-oracle +++ b/flext-target-oracle @@ -1 +1 @@ -Subproject commit 07228747ff6c41ebb9eaaea4f25dee51a8b47c72 +Subproject commit 462c11a6c61995c6e8626faeb264964d2181b183 diff --git a/flext-target-oracle-oic b/flext-target-oracle-oic index 52514a16..c9ed42bc 160000 --- a/flext-target-oracle-oic +++ b/flext-target-oracle-oic @@ -1 +1 @@ -Subproject commit 52514a1699da146e03ffcecfcb0bd46b9daedb3d +Subproject commit c9ed42bc29ff670ef3c79666ecb29a585933e85e diff --git a/flext-target-oracle-wms b/flext-target-oracle-wms index c2ec5e67..e15210d6 160000 --- a/flext-target-oracle-wms +++ b/flext-target-oracle-wms @@ -1 +1 @@ -Subproject commit c2ec5e67bf9b841a915721b810018ec34e730802 +Subproject commit e15210d688ab1e03ce4bad511e95be60b2d28f64 diff --git a/flext-web b/flext-web index 3b1787fe..8cc6df48 160000 --- a/flext-web +++ b/flext-web @@ -1 +1 @@ -Subproject commit 3b1787fe9bc889ead2eb2b85d4736b34f96c6d0c +Subproject commit 8cc6df488c1b1ebe22e311e9c74a7dbd5f7ecce4 diff --git a/scripts/maintenance/enforce_python_version.py b/scripts/maintenance/enforce_python_version.py new file mode 100644 index 00000000..9606966d --- /dev/null +++ b/scripts/maintenance/enforce_python_version.py @@ -0,0 +1,247 @@ +#!/usr/bin/env python3 +# Owner-Skill: .claude/skills/workspace-maintenance/SKILL.md +"""Enforce Python version constraints across all workspace projects. + +Creates .python-version files and injects conftest.py version guards +to prevent venv creation with wrong Python interpreter. + +Usage:: + + python scripts/maintenance/enforce_python_version.py [--check] [--verbose] + +Modes: + (default) Apply: create .python-version, inject conftest guards + --check Verify: exit non-zero if any project is missing guards +""" + +from __future__ import annotations + +import argparse +import re +import sys +from pathlib import Path + +ROOT = Path(__file__).resolve().parents[2] +REQUIRED_MINOR = 13 +PYTHON_VERSION_CONTENT = f"3.{REQUIRED_MINOR}\n" + +# Marker comment used to detect if the guard is already injected +GUARD_MARKER = "# PYTHON_VERSION_GUARD" + +# The guard block injected into conftest.py files +GUARD_BLOCK = f"""\ +{GUARD_MARKER} — Do not remove. Managed by scripts/maintenance/enforce_python_version.py +import sys as _sys + +if _sys.version_info[:2] != (3, {REQUIRED_MINOR}): + _v = f"{{_sys.version_info.major}}.{{_sys.version_info.minor}}.{{_sys.version_info.micro}}" + raise RuntimeError( + f"\\n{{'=' * 72}}\\n" + f"FATAL: Python {{_v}} detected — this project requires Python 3.{REQUIRED_MINOR}.\\n" + f"\\n" + f"The virtual environment was created with the WRONG Python interpreter.\\n" + f"\\n" + f"Fix:\\n" + f" 1. rm -rf .venv\\n" + f" 2. poetry env use python3.{REQUIRED_MINOR}\\n" + f" 3. poetry install\\n" + f"\\n" + f"Or use the workspace Makefile:\\n" + f" make setup PROJECT=\\n" + f"{{'=' * 72}}\\n" + ) +del _sys +{GUARD_MARKER}_END +""" + + +def _discover_projects(workspace_root: Path) -> list[Path]: + """Discover all flext-* projects with pyproject.toml.""" + projects: list[Path] = [] + for entry in sorted(workspace_root.iterdir(), key=lambda v: v.name): + if not entry.is_dir() or not entry.name.startswith("flext-"): + continue + if (entry / "pyproject.toml").exists(): + projects.append(entry) + return projects + + +def _ensure_python_version_file( + project: Path, *, check_only: bool, verbose: bool +) -> bool: + """Ensure .python-version exists with correct content.""" + pv_file = project / ".python-version" + if pv_file.exists(): + content = pv_file.read_text(encoding="utf-8").strip() + if content == f"3.{REQUIRED_MINOR}": + if verbose: + print(f" ✓ .python-version OK: {project.name}") + return True + if check_only: + print(f" ✗ .python-version WRONG ({content}): {project.name}") + return False + if verbose: + print( + f" ↻ .python-version FIXED ({content} → 3.{REQUIRED_MINOR}): {project.name}" + ) + else: + if check_only: + print(f" ✗ .python-version MISSING: {project.name}") + return False + if verbose: + print(f" + .python-version CREATED: {project.name}") + + _ = pv_file.write_text(PYTHON_VERSION_CONTENT, encoding="utf-8") + return True + + +def _has_guard(content: str) -> bool: + """Check if conftest.py already has the version guard.""" + return GUARD_MARKER in content + + +def _remove_existing_guard(content: str) -> str: + """Remove existing guard block (for replacement).""" + pattern = re.compile( + rf"^{re.escape(GUARD_MARKER)}.*?^{re.escape(GUARD_MARKER)}_END\n?", + re.MULTILINE | re.DOTALL, + ) + return pattern.sub("", content) + + +def _inject_guard(content: str) -> str: + """Inject version guard after the module docstring, before other imports.""" + # Remove any existing guard first + content = _remove_existing_guard(content) + + # Find insertion point: after module docstring, before first import + # Strategy: find the end of the docstring block, insert guard there + lines = content.split("\n") + insert_idx = 0 + + # Skip shebang + if lines and lines[0].startswith("#!"): + insert_idx = 1 + + # Skip leading comments + while insert_idx < len(lines) and lines[insert_idx].startswith("#"): + insert_idx += 1 + + # Skip blank lines + while insert_idx < len(lines) and not lines[insert_idx].strip(): + insert_idx += 1 + + # Skip docstring (triple-quoted) + if insert_idx < len(lines): + line = lines[insert_idx].strip() + if line.startswith('"""') or line.startswith("'''"): + quote = line[:3] + # Check if single-line docstring + if line.count(quote) >= 2 and len(line) > 3: + insert_idx += 1 + else: + # Multi-line docstring — find closing quotes + insert_idx += 1 + while insert_idx < len(lines) and quote not in lines[insert_idx]: + insert_idx += 1 + if insert_idx < len(lines): + insert_idx += 1 + + # Skip blank lines after docstring + while insert_idx < len(lines) and not lines[insert_idx].strip(): + insert_idx += 1 + + # Skip __future__ imports (must come before guard) + while insert_idx < len(lines) and lines[insert_idx].strip().startswith( + "from __future__" + ): + insert_idx += 1 + + # Skip blank lines after __future__ + while insert_idx < len(lines) and not lines[insert_idx].strip(): + insert_idx += 1 + + # Insert guard + before = "\n".join(lines[:insert_idx]) + after = "\n".join(lines[insert_idx:]) + + if before and not before.endswith("\n"): + before += "\n" + + return f"{before}{GUARD_BLOCK}\n{after}" + + +def _ensure_conftest_guard(project: Path, *, check_only: bool, verbose: bool) -> bool: + """Ensure tests/conftest.py has the Python version guard.""" + conftest = project / "tests" / "conftest.py" + + if not conftest.exists(): + if verbose: + print(f" - No tests/conftest.py: {project.name} (skipped)") + return True # Not a failure — project might not have tests + + content = conftest.read_text(encoding="utf-8") + + if _has_guard(content): + if verbose: + print(f" ✓ conftest.py guard OK: {project.name}") + return True + + if check_only: + print(f" ✗ conftest.py guard MISSING: {project.name}") + return False + + new_content = _inject_guard(content) + _ = conftest.write_text(new_content, encoding="utf-8") + if verbose: + print(f" + conftest.py guard INJECTED: {project.name}") + return True + + +def main(argv: list[str] | None = None) -> int: + """Run enforcement.""" + parser = argparse.ArgumentParser(description="Enforce Python version constraints") + _ = parser.add_argument( + "--check", action="store_true", help="Check mode (no writes)" + ) + _ = parser.add_argument( + "--verbose", "-v", action="store_true", help="Verbose output" + ) + args = parser.parse_args(argv) + + projects = _discover_projects(ROOT) + all_ok = True + mode = "Checking" if args.check else "Enforcing" + + print(f"{mode} Python 3.{REQUIRED_MINOR} for {len(projects)} projects...") + + # Workspace root .python-version + if not _ensure_python_version_file( + ROOT, check_only=args.check, verbose=args.verbose + ): + all_ok = False + + for project in projects: + if not _ensure_python_version_file( + project, check_only=args.check, verbose=args.verbose + ): + all_ok = False + if not _ensure_conftest_guard( + project, check_only=args.check, verbose=args.verbose + ): + all_ok = False + + if all_ok: + print(f"✓ All {len(projects)} projects enforce Python 3.{REQUIRED_MINOR}") + return 0 + + if args.check: + print(f"✗ Some projects missing Python 3.{REQUIRED_MINOR} enforcement") + print(f" Run: python scripts/maintenance/enforce_python_version.py") + return 1 + + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/scripts/release/build.py b/scripts/release/build.py index 09519406..bc0a2ee0 100644 --- a/scripts/release/build.py +++ b/scripts/release/build.py @@ -11,7 +11,7 @@ if str(SCRIPTS_ROOT) not in sys.path: sys.path.insert(0, str(SCRIPTS_ROOT)) -from release.shared import discover_projects, workspace_root +from release.shared import resolve_projects, workspace_root def _parse_args() -> argparse.Namespace: @@ -19,6 +19,7 @@ def _parse_args() -> argparse.Namespace: _ = parser.add_argument("--root", type=Path, default=Path(".")) _ = parser.add_argument("--version", required=True) _ = parser.add_argument("--output-dir", type=Path, required=True) + _ = parser.add_argument("--projects", nargs="*", default=[]) return parser.parse_args() @@ -38,12 +39,10 @@ def main() -> int: output_dir.mkdir(parents=True, exist_ok=True) report_path = output_dir / "build-report.json" - projects = discover_projects(root) + projects = resolve_projects(root, args.projects) targets = [ ("root", root), - ("algar-oud-mig", root / "algar-oud-mig"), *[(project.name, project.path) for project in projects], - ("gruponos-meltano-native", root / "gruponos-meltano-native"), ] seen: set[str] = set() diff --git a/scripts/release/changelog.py b/scripts/release/changelog.py index 4d01ea59..c056b958 100644 --- a/scripts/release/changelog.py +++ b/scripts/release/changelog.py @@ -25,13 +25,14 @@ def _parse_args() -> argparse.Namespace: def _update_changelog(existing: str, version: str, tag: str) -> str: date = datetime.now(UTC).date().isoformat() + heading = f"## {version} - " section = ( - f"## {version} - {date}\n\n" + f"{heading}{date}\n\n" f"- Workspace release tag: `{tag}`\n" "- Status: Alpha, non-production\n\n" f"Full notes: `docs/releases/{tag}.md`\n\n" ) - if section in existing: + if heading in existing: return existing marker = "# Changelog\n\n" if marker in existing: diff --git a/scripts/release/notes.py b/scripts/release/notes.py index 50741f64..0fa8c547 100644 --- a/scripts/release/notes.py +++ b/scripts/release/notes.py @@ -9,7 +9,7 @@ if str(SCRIPTS_ROOT) not in sys.path: sys.path.insert(0, str(SCRIPTS_ROOT)) -from release.shared import discover_projects, run_capture, workspace_root +from release.shared import resolve_projects, run_capture, workspace_root def _parse_args() -> argparse.Namespace: @@ -18,6 +18,7 @@ def _parse_args() -> argparse.Namespace: _ = parser.add_argument("--tag", required=True) _ = parser.add_argument("--output", type=Path, required=True) _ = parser.add_argument("--version", default="") + _ = parser.add_argument("--projects", nargs="*", default=[]) return parser.parse_args() @@ -56,7 +57,7 @@ def main() -> int: previous = _previous_tag(root, args.tag) changes = _collect_changes(root, previous, args.tag) - projects = discover_projects(root) + projects = resolve_projects(root, args.projects) version = args.version or args.tag.removeprefix("v") lines: list[str] = [ @@ -70,7 +71,7 @@ def main() -> int: "## Scope", "", f"- Workspace release version: {version}", - f"- Projects packaged: {len(projects) + 2}", + f"- Projects packaged: {len(projects) + 1}", "", "## Projects impacted", "", @@ -79,9 +80,7 @@ def main() -> int: f"- {name}" for name in [ "root", - "algar-oud-mig", *[project.name for project in projects], - "gruponos-meltano-native", ] ) lines.extend([ diff --git a/scripts/release/run.py b/scripts/release/run.py index acaad58b..36758acf 100644 --- a/scripts/release/run.py +++ b/scripts/release/run.py @@ -2,9 +2,9 @@ from __future__ import annotations import argparse -import re from pathlib import Path import sys +import tomllib SCRIPTS_ROOT = Path(__file__).resolve().parents[1] if str(SCRIPTS_ROOT) not in sys.path: @@ -12,8 +12,8 @@ from release.shared import ( bump_version, - discover_projects, parse_semver, + resolve_projects, run_capture, run_checked, workspace_root, @@ -31,17 +31,21 @@ def _parse_args() -> argparse.Namespace: _ = parser.add_argument("--push", type=int, default=0) _ = parser.add_argument("--dry-run", type=int, default=0) _ = parser.add_argument("--create-branches", type=int, default=1) + _ = parser.add_argument("--projects", nargs="*", default=[]) return parser.parse_args() def _current_version(root: Path) -> str: pyproject = root / "pyproject.toml" - content = pyproject.read_text(encoding="utf-8") - match = re.search(r'^version\s*=\s*"(?P[^"]+)"', content, flags=re.M) - if not match: + content = pyproject.read_bytes() + data = tomllib.loads(content.decode("utf-8")) + project = data.get("project") + if not isinstance(project, dict): + raise RuntimeError("unable to detect [project] section from pyproject.toml") + version = project.get("version") + if not isinstance(version, str) or not version: raise RuntimeError("unable to detect version from pyproject.toml") - value = match.group("version") - return value.removesuffix("-dev") + return version.removesuffix("-dev") def _resolve_version(args: argparse.Namespace, root: Path) -> str: @@ -71,18 +75,18 @@ def _resolve_tag(args: argparse.Namespace, version: str) -> str: return f"v{version}" -def _create_release_branches(root: Path, version: str) -> None: +def _create_release_branches( + root: Path, version: str, selected_projects: list[Path] +) -> None: branch = f"release/{version}" run_checked(["git", "checkout", "-B", branch], cwd=root) - for project in discover_projects(root): - run_checked(["git", "checkout", "-B", branch], cwd=project.path) - for extra in ("algar-oud-mig", "gruponos-meltano-native"): - project_root = root / extra - if project_root.exists(): - run_checked(["git", "checkout", "-B", branch], cwd=project_root) + for project_path in selected_projects: + run_checked(["git", "checkout", "-B", branch], cwd=project_path) -def _phase_version(root: Path, version: str, dry_run: bool) -> None: +def _phase_version( + root: Path, version: str, dry_run: bool, project_names: list[str] +) -> None: command = [ "python", "scripts/release/version.py", @@ -92,6 +96,8 @@ def _phase_version(root: Path, version: str, dry_run: bool) -> None: version, "--check" if dry_run else "--apply", ] + if project_names: + command.extend(["--projects", *project_names]) run_checked(command, cwd=root) @@ -99,43 +105,48 @@ def _phase_validate(root: Path) -> None: run_checked(["make", "validate", "VALIDATE_SCOPE=workspace"], cwd=root) -def _phase_build(root: Path, version: str) -> None: +def _phase_build(root: Path, version: str, project_names: list[str]) -> None: output = root / ".reports" / "release" / f"v{version}" - run_checked( - [ - "python", - "scripts/release/build.py", - "--root", - str(root), - "--version", - version, - "--output-dir", - str(output), - ], - cwd=root, - ) + command = [ + "python", + "scripts/release/build.py", + "--root", + str(root), + "--version", + version, + "--output-dir", + str(output), + ] + if project_names: + command.extend(["--projects", *project_names]) + run_checked(command, cwd=root) def _phase_publish( - root: Path, version: str, tag: str, push: bool, dry_run: bool + root: Path, + version: str, + tag: str, + push: bool, + dry_run: bool, + project_names: list[str], ) -> None: notes = root / ".reports" / "release" / tag / "RELEASE_NOTES.md" notes.parent.mkdir(parents=True, exist_ok=True) - run_checked( - [ - "python", - "scripts/release/notes.py", - "--root", - str(root), - "--tag", - tag, - "--version", - version, - "--output", - str(notes), - ], - cwd=root, - ) + command = [ + "python", + "scripts/release/notes.py", + "--root", + str(root), + "--tag", + tag, + "--version", + version, + "--output", + str(notes), + ] + if project_names: + command.extend(["--projects", *project_names]) + run_checked(command, cwd=root) if not dry_run: run_checked( [ @@ -164,6 +175,9 @@ def _phase_publish( def main() -> int: args = _parse_args() root = workspace_root(args.root) + selected_projects = resolve_projects(root, args.projects) + selected_project_names = [project.name for project in selected_projects] + selected_project_paths = [project.path for project in selected_projects] version = _resolve_version(args, root) tag = _resolve_tag(args, version) phases = ( @@ -175,22 +189,30 @@ def main() -> int: _ = print(f"release_version={version}") _ = print(f"release_tag={tag}") _ = print(f"phases={','.join(phases)}") + _ = print(f"projects={','.join(selected_project_names)}") if args.create_branches == 1 and args.dry_run == 0: - _create_release_branches(root, version) + _create_release_branches(root, version, selected_project_paths) for phase in phases: if phase == "validate": _phase_validate(root) continue if phase == "version": - _phase_version(root, version, args.dry_run == 1) + _phase_version(root, version, args.dry_run == 1, selected_project_names) continue if phase == "build": - _phase_build(root, version) + _phase_build(root, version, selected_project_names) continue if phase == "publish": - _phase_publish(root, version, tag, args.push == 1, args.dry_run == 1) + _phase_publish( + root, + version, + tag, + args.push == 1, + args.dry_run == 1, + selected_project_names, + ) continue raise RuntimeError(f"invalid phase: {phase}") diff --git a/scripts/release/shared.py b/scripts/release/shared.py index 0598b719..5c1d5c81 100644 --- a/scripts/release/shared.py +++ b/scripts/release/shared.py @@ -4,6 +4,7 @@ import json import re +import shlex import subprocess import sys from dataclasses import dataclass @@ -54,6 +55,21 @@ def discover_projects(root: Path) -> list[Project]: return sorted(projects, key=lambda project: project.name) +def resolve_projects(root: Path, names: list[str]) -> list[Project]: + projects = discover_projects(root) + if not names: + return projects + + by_name = {project.name: project for project in projects} + missing = [name for name in names if name not in by_name] + if missing: + missing_text = ", ".join(sorted(missing)) + raise RuntimeError(f"unknown release projects: {missing_text}") + + resolved = [by_name[name] for name in names] + return sorted(resolved, key=lambda project: project.name) + + def parse_semver(version: str) -> tuple[int, int, int]: match = SEMVER_RE.match(version) if not match: @@ -79,7 +95,7 @@ def bump_version(current_version: str, bump: str) -> str: def run_checked(command: list[str], cwd: Path | None = None) -> None: result = subprocess.run(command, cwd=cwd, check=False) if result.returncode != 0: - cmd = " ".join(command) + cmd = shlex.join(command) raise RuntimeError(f"command failed ({result.returncode}): {cmd}") @@ -88,7 +104,7 @@ def run_capture(command: list[str], cwd: Path | None = None) -> str: command, cwd=cwd, capture_output=True, text=True, check=False ) if result.returncode != 0: - cmd = " ".join(command) + cmd = shlex.join(command) detail = (result.stderr or result.stdout).strip() raise RuntimeError(f"command failed ({result.returncode}): {cmd}: {detail}") return result.stdout.strip() diff --git a/scripts/release/version.py b/scripts/release/version.py index 48f49775..3851de0f 100644 --- a/scripts/release/version.py +++ b/scripts/release/version.py @@ -3,49 +3,50 @@ import argparse from pathlib import Path +import re import sys SCRIPTS_ROOT = Path(__file__).resolve().parents[1] if str(SCRIPTS_ROOT) not in sys.path: sys.path.insert(0, str(SCRIPTS_ROOT)) -from release.shared import discover_projects, parse_semver, workspace_root +from release.shared import parse_semver, resolve_projects, workspace_root def _replace_version(content: str, version: str) -> tuple[str, bool]: - old = 'version = "0.10.0-dev"' - new = f'version = "{version}"' - if old in content: - return content.replace(old, new), True - - marker = 'version = "' - start = content.find(marker) - if start < 0: + project_match = re.search(r"(?ms)^\[project\]\n(?P.*?)(?:^\[|\Z)", content) + if not project_match: return content, False - value_start = start + len(marker) - value_end = content.find('"', value_start) - if value_end < 0: + + body = project_match.group("body") + version_match = re.search(r'(?m)^version\s*=\s*"(?P[^"]+)"\s*$', body) + if not version_match: return content, False - current = content[value_start:value_end] + current = version_match.group("value") current_clean = current.removesuffix("-dev") _ = parse_semver(current_clean) if current == version: return content, False - updated = content[:value_start] + version + content[value_end:] - return updated, True + + replacement = f'version = "{version}"' + updated_body = re.sub( + r'(?m)^version\s*=\s*"[^"]+"\s*$', + replacement, + body, + count=1, + ) + start, end = project_match.span("body") + updated = content[:start] + updated_body + content[end:] + return updated, updated != content -def _version_files(root: Path) -> list[Path]: +def _version_files(root: Path, project_names: list[str]) -> list[Path]: files: list[Path] = [root / "pyproject.toml"] - for project in discover_projects(root): + for project in resolve_projects(root, project_names): pyproject = project.path / "pyproject.toml" if pyproject.exists(): files.append(pyproject) - for extra in ("algar-oud-mig", "gruponos-meltano-native"): - pyproject = root / extra / "pyproject.toml" - if pyproject.exists(): - files.append(pyproject) dedup = sorted({path.resolve() for path in files}) return dedup @@ -54,6 +55,7 @@ def _parse_args() -> argparse.Namespace: parser = argparse.ArgumentParser() _ = parser.add_argument("--root", type=Path, default=Path(".")) _ = parser.add_argument("--version", required=True) + _ = parser.add_argument("--projects", nargs="*", default=[]) _ = parser.add_argument("--apply", action="store_true") _ = parser.add_argument("--check", action="store_true") return parser.parse_args() @@ -65,7 +67,7 @@ def main() -> int: _ = parse_semver(args.version) changed = 0 - for file_path in _version_files(root): + for file_path in _version_files(root, args.projects): content = file_path.read_text(encoding="utf-8") updated, did_change = _replace_version(content, args.version) if did_change: diff --git a/tests/scripts/release/test_release_scripts.py b/tests/scripts/release/test_release_scripts.py new file mode 100644 index 00000000..904a0ada --- /dev/null +++ b/tests/scripts/release/test_release_scripts.py @@ -0,0 +1,46 @@ +from __future__ import annotations + +import importlib.util +import re +import sys +from pathlib import Path +from typing import Any + + +def _load_module(relative_path: str, module_name: str) -> Any: + module_path = Path(__file__).resolve().parents[3] / relative_path + spec = importlib.util.spec_from_file_location(module_name, module_path) + assert spec is not None + assert spec.loader is not None + module = importlib.util.module_from_spec(spec) + sys.modules[spec.name] = module + spec.loader.exec_module(module) + return module + + +def test_replace_version_updates_only_project_version() -> None: + mod = _load_module("scripts/release/version.py", "release_version") + content = """ +[project] +name = "demo" +version = "0.11.0-dev" + +[tool.poetry.dependencies] +python = ">=3.13,<4.0" +flext-core = "0.11.0-dev" +""".strip() + + updated, did_change = mod._replace_version(content, "0.11.0") + + assert did_change is True + assert 'version = "0.11.0"' in updated + assert 'flext-core = "0.11.0-dev"' in updated + + +def test_update_changelog_is_idempotent_by_version_heading() -> None: + mod = _load_module("scripts/release/changelog.py", "release_changelog") + first = mod._update_changelog("# Changelog\n\n", "0.11.0", "v0.11.0") + second = mod._update_changelog(first, "0.11.0", "v0.11.0") + + assert first == second + assert len(re.findall(r"^## 0\.11\.0 - ", second, flags=re.M)) == 1 diff --git a/tests/scripts/release/test_release_shared_and_run.py b/tests/scripts/release/test_release_shared_and_run.py new file mode 100644 index 00000000..9b7437f0 --- /dev/null +++ b/tests/scripts/release/test_release_shared_and_run.py @@ -0,0 +1,77 @@ +from __future__ import annotations + +import importlib.util +import json +import sys +from pathlib import Path +from types import SimpleNamespace +from typing import Any + +import pytest + + +def _load_module(module_name: str, relative_path: str) -> Any: + module_path = Path(__file__).resolve().parents[3] / relative_path + spec = importlib.util.spec_from_file_location(module_name, module_path) + assert spec is not None + assert spec.loader is not None + module = importlib.util.module_from_spec(spec) + sys.modules[spec.name] = module + spec.loader.exec_module(module) + return module + + +def test_resolve_projects_uses_auto_discovery(monkeypatch: pytest.MonkeyPatch) -> None: + shared = _load_module("release_shared", "scripts/release/shared.py") + payload = { + "projects": [ + {"name": "flext-api", "path": "/tmp/ws/flext-api", "kind": "submodule"}, + { + "name": "external-tool", + "path": "/tmp/ws/external-tool", + "kind": "external", + }, + ] + } + + def _fake_run(*_args: object, **_kwargs: object) -> SimpleNamespace: + return SimpleNamespace(returncode=0, stdout=json.dumps(payload), stderr="") + + monkeypatch.setattr(shared.subprocess, "run", _fake_run) + + projects = shared.resolve_projects(Path("/tmp/ws"), []) + assert [project.name for project in projects] == ["external-tool", "flext-api"] + + +def test_resolve_projects_rejects_unknown(monkeypatch: pytest.MonkeyPatch) -> None: + shared = _load_module("release_shared_unknown", "scripts/release/shared.py") + payload = { + "projects": [ + {"name": "flext-api", "path": "/tmp/ws/flext-api", "kind": "submodule"}, + ] + } + + def _fake_run(*_args: object, **_kwargs: object) -> SimpleNamespace: + return SimpleNamespace(returncode=0, stdout=json.dumps(payload), stderr="") + + monkeypatch.setattr(shared.subprocess, "run", _fake_run) + + with pytest.raises(RuntimeError, match="unknown release projects"): + _ = shared.resolve_projects(Path("/tmp/ws"), ["missing-project"]) + + +def test_current_version_reads_project_table(tmp_path: Path) -> None: + run_mod = _load_module("release_run", "scripts/release/run.py") + pyproject = tmp_path / "pyproject.toml" + _ = pyproject.write_text( + """[tool.sample] +version = "999.999.999" + +[project] +name = "demo" +version = "0.10.0-dev" +""", + encoding="utf-8", + ) + + assert run_mod._current_version(tmp_path) == "0.10.0" From 2b3b5074ac0ef52234142e08e0bd603c2cc50fd8 Mon Sep 17 00:00:00 2001 From: Marlon Costa Date: Fri, 20 Feb 2026 13:02:42 -0300 Subject: [PATCH 02/34] feat(pr): add unified make pr workflow manager --- Makefile | 37 ++++- base.mk | 35 ++++- flexcore | 2 +- flext-api | 2 +- flext-auth | 2 +- flext-cli | 2 +- flext-core | 2 +- flext-db-oracle | 2 +- flext-dbt-ldap | 2 +- flext-dbt-ldif | 2 +- flext-dbt-oracle | 2 +- flext-dbt-oracle-wms | 2 +- flext-grpc | 2 +- flext-ldap | 2 +- flext-ldif | 2 +- flext-meltano | 2 +- flext-observability | 2 +- flext-oracle-oic | 2 +- flext-oracle-wms | 2 +- flext-plugin | 2 +- flext-quality | 2 +- flext-tap-ldap | 2 +- flext-tap-ldif | 2 +- flext-tap-oracle | 2 +- flext-tap-oracle-oic | 2 +- flext-tap-oracle-wms | 2 +- flext-target-ldap | 2 +- flext-target-ldif | 2 +- flext-target-oracle | 2 +- flext-target-oracle-oic | 2 +- flext-target-oracle-wms | 2 +- flext-web | 2 +- scripts/github/pr_manager.py | 199 ++++++++++++++++++++++++ tests/scripts/github/test_pr_manager.py | 80 ++++++++++ 34 files changed, 378 insertions(+), 33 deletions(-) create mode 100644 scripts/github/pr_manager.py create mode 100644 tests/scripts/github/test_pr_manager.py diff --git a/Makefile b/Makefile index 59f4040e..0552ae4d 100644 --- a/Makefile +++ b/Makefile @@ -23,6 +23,16 @@ VERSION ?= TAG ?= BUMP ?= CREATE_BRANCHES ?= 1 +PR_ACTION ?= status +PR_BASE ?= main +PR_HEAD ?= +PR_NUMBER ?= +PR_TITLE ?= +PR_BODY ?= +PR_DRAFT ?= 0 +PR_MERGE_METHOD ?= squash +PR_AUTO ?= 0 +PR_DELETE_BRANCH ?= 0 Q := @ ifdef VERBOSE @@ -121,7 +131,7 @@ if [ -n "$$residual_venvs" ]; then \ fi endef -.PHONY: help setup upgrade build check security format docs test validate typings clean release release-ci +.PHONY: help setup upgrade build check security format docs test validate typings clean release release-ci pr help: ## Show simple workspace verbs $(Q)echo "FLEXT Workspace" @@ -141,6 +151,7 @@ help: ## Show simple workspace verbs $(Q)echo " validate Run validate gates (FIX=1 auto-fix, VALIDATE_SCOPE=workspace for repo-level)" $(Q)echo " release Interactive workspace release orchestration" $(Q)echo " release-ci Non-interactive release run for CI/tag workflows" + $(Q)echo " pr Manage PRs for selected projects" $(Q)echo " typings Stub supply-chain + typing report (PROJECT/PROJECTS to scope)" $(Q)echo " clean Clean all projects" $(Q)echo "" @@ -160,6 +171,10 @@ help: ## Show simple workspace verbs $(Q)echo " PUSH=1 Push release commit/tag" $(Q)echo " VERSION= TAG=v BUMP=patch Release controls" $(Q)echo " CREATE_BRANCHES=1|0 Create release branches in workspace + projects" + $(Q)echo " PR_ACTION=status|create|view|checks|merge|close" + $(Q)echo " PR_BASE=main PR_HEAD= PR_NUMBER= PR_DRAFT=0|1" + $(Q)echo " PR_TITLE='title' PR_BODY='body' PR_MERGE_METHOD=squash|merge|rebase" + $(Q)echo " PR_AUTO=0|1 PR_DELETE_BRANCH=0|1" $(Q)echo " DEPS_REPORT=0 Skip dependency report after upgrade/typings" $(Q)echo "" $(Q)echo "Examples:" @@ -172,6 +187,8 @@ help: ## Show simple workspace verbs $(Q)echo " make validate VALIDATE_SCOPE=workspace" $(Q)echo " make release BUMP=minor" $(Q)echo " make release-ci VERSION=0.11.0 TAG=v0.11.0 RELEASE_PHASE=all" + $(Q)echo " make pr PROJECT=flext-core PR_ACTION=status" + $(Q)echo " make pr PROJECT=flext-core PR_ACTION=create PR_TITLE='release: 0.11.0-dev'" $(Q)echo " NOTE: External projects (not in .gitmodules) require manual clone." setup: ## Install all projects into workspace .venv @@ -435,6 +452,24 @@ release-ci: ## Non-interactive release run for CI/tag workflows $(if $(TAG),--tag "$(TAG)",) \ $(if $(BUMP),--bump "$(BUMP)",) +pr: ## Manage pull requests for selected projects + $(Q)$(ENSURE_NO_PROJECT_CONFLICT) + $(Q)$(ENSURE_SELECTED_PROJECTS) + $(Q)$(ENSURE_PROJECTS_EXIST) + $(Q)$(ORCHESTRATOR) --verb pr \ + $(if $(filter 1,$(FAIL_FAST)),--fail-fast) \ + --make-arg "PR_ACTION=$(PR_ACTION)" \ + --make-arg "PR_BASE=$(PR_BASE)" \ + $(if $(PR_HEAD),--make-arg "PR_HEAD=$(PR_HEAD)",) \ + $(if $(PR_NUMBER),--make-arg "PR_NUMBER=$(PR_NUMBER)",) \ + $(if $(PR_TITLE),--make-arg "PR_TITLE=$(PR_TITLE)",) \ + $(if $(PR_BODY),--make-arg "PR_BODY=$(PR_BODY)",) \ + --make-arg "PR_DRAFT=$(PR_DRAFT)" \ + --make-arg "PR_MERGE_METHOD=$(PR_MERGE_METHOD)" \ + --make-arg "PR_AUTO=$(PR_AUTO)" \ + --make-arg "PR_DELETE_BRANCH=$(PR_DELETE_BRANCH)" \ + $(SELECTED_PROJECTS) + security: ## Run all security checks in all projects $(Q)$(ENSURE_NO_PROJECT_CONFLICT) $(Q)$(ENFORCE_WORKSPACE_VENV) diff --git a/base.mk b/base.mk index 77541d10..adc8531f 100644 --- a/base.mk +++ b/base.mk @@ -19,6 +19,16 @@ CHECK_GATES ?= VALIDATE_GATES ?= DOCS_PHASE ?= all AUTO_ADJUST ?= 1 +PR_ACTION ?= status +PR_BASE ?= main +PR_HEAD ?= +PR_NUMBER ?= +PR_TITLE ?= +PR_BODY ?= +PR_DRAFT ?= 0 +PR_MERGE_METHOD ?= squash +PR_AUTO ?= 0 +PR_DELETE_BRANCH ?= 0 PYTEST_REPORT_ARGS := -ra --durations=25 --durations-min=0.001 --tb=short PYTEST_DIAG_ARGS := -rA --durations=0 --tb=long --showlocals @@ -89,8 +99,8 @@ $(LINT_CACHE_DIR): $(Q)mkdir -p $(LINT_CACHE_DIR) # === SIMPLE VERB SURFACE === -.PHONY: help setup build check security format docs docs-base docs-sync-scripts test validate clean _preflight -STANDARD_VERBS := setup build check security format docs test validate clean +.PHONY: help setup build check security format docs docs-base docs-sync-scripts test validate clean pr _preflight +STANDARD_VERBS := setup build check security format docs test validate clean pr $(STANDARD_VERBS): _preflight define ENFORCE_WORKSPACE_VENV @@ -170,7 +180,14 @@ help: ## Show commands $(Q)echo " docs Build docs" $(Q)echo " test Run pytest only" $(Q)echo " validate Run validate gates only (use FIX=1 to auto-fix first)" + $(Q)echo " pr Manage this repository PR (default: status)" $(Q)echo " clean Clean build/test/type artifacts" + $(Q)echo "" + $(Q)echo "PR variables:" + $(Q)echo " PR_ACTION=status|create|view|checks|merge|close" + $(Q)echo " PR_BASE=main PR_HEAD= PR_NUMBER=" + $(Q)echo " PR_TITLE='title' PR_BODY='body' PR_DRAFT=0|1" + $(Q)echo " PR_MERGE_METHOD=squash|merge|rebase PR_AUTO=0|1 PR_DELETE_BRANCH=0|1" setup: ## Complete setup $(Q)if [ "$(CORE_STACK)" = "go" ]; then \ @@ -484,6 +501,20 @@ validate: ## Run validate gates (VALIDATE_GATES=complexity,docstring to select, $(POETRY) run interrogate $(SRC_DIR) --fail-under=$(DOCSTRING_MIN) --ignore-init-method --ignore-magic -q; \ fi +pr: ## Manage pull requests for this repository + $(Q)python3 "$(WORKSPACE_ROOT)/scripts/github/pr_manager.py" \ + --repo-root "$(CURDIR)" \ + --action "$(PR_ACTION)" \ + --base "$(PR_BASE)" \ + $(if $(PR_HEAD),--head "$(PR_HEAD)",) \ + $(if $(PR_NUMBER),--number "$(PR_NUMBER)",) \ + $(if $(PR_TITLE),--title "$(PR_TITLE)",) \ + $(if $(PR_BODY),--body "$(PR_BODY)",) \ + --draft "$(PR_DRAFT)" \ + --merge-method "$(PR_MERGE_METHOD)" \ + --auto "$(PR_AUTO)" \ + --delete-branch "$(PR_DELETE_BRANCH)" + clean: ## Clean artifacts $(Q)if [ "$(CORE_STACK)" = "go" ]; then \ rm -f coverage.out coverage.html; \ diff --git a/flexcore b/flexcore index abf96234..34eaebf7 160000 --- a/flexcore +++ b/flexcore @@ -1 +1 @@ -Subproject commit abf96234a51ff6d45f033b5fd1d71d3aedcde7ca +Subproject commit 34eaebf71e72a609e5d3a753e2d28a7865723f55 diff --git a/flext-api b/flext-api index 391e5ced..228874ad 160000 --- a/flext-api +++ b/flext-api @@ -1 +1 @@ -Subproject commit 391e5ced5e7d302fac311aee183817094facd545 +Subproject commit 228874ad5991dc17d24b39bc20aecf580cdb3b96 diff --git a/flext-auth b/flext-auth index 023e06d9..bdbf86f3 160000 --- a/flext-auth +++ b/flext-auth @@ -1 +1 @@ -Subproject commit 023e06d9c32789d8c82f4661f4c617ab18daf8b0 +Subproject commit bdbf86f3434904004c225a36991f10bef332738d diff --git a/flext-cli b/flext-cli index e6240a04..82ccd5df 160000 --- a/flext-cli +++ b/flext-cli @@ -1 +1 @@ -Subproject commit e6240a04c781ff31681db919ee8efdd4cd565209 +Subproject commit 82ccd5dfa4bba93651ec6eba9525e372ea068a0f diff --git a/flext-core b/flext-core index 281a4c0c..43855380 160000 --- a/flext-core +++ b/flext-core @@ -1 +1 @@ -Subproject commit 281a4c0c93808155a09390501456ee9e09c4cd6b +Subproject commit 43855380f19f5cee1aaaa23fbefaada453648bd5 diff --git a/flext-db-oracle b/flext-db-oracle index d059e818..af67e948 160000 --- a/flext-db-oracle +++ b/flext-db-oracle @@ -1 +1 @@ -Subproject commit d059e8180d6f6a110cdb0a252c1974fcecc91b42 +Subproject commit af67e9480e6f7026f9865faf8f2d73594f31aa37 diff --git a/flext-dbt-ldap b/flext-dbt-ldap index 3fd7465c..0a7484bd 160000 --- a/flext-dbt-ldap +++ b/flext-dbt-ldap @@ -1 +1 @@ -Subproject commit 3fd7465cd146e21a4149904c88c14af900db5414 +Subproject commit 0a7484bdd002167973a5bbe87dc4e8d160f05e4a diff --git a/flext-dbt-ldif b/flext-dbt-ldif index b00de7b5..1e41e0b7 160000 --- a/flext-dbt-ldif +++ b/flext-dbt-ldif @@ -1 +1 @@ -Subproject commit b00de7b59df70aa038d54e0d5bb3a58595f62b5a +Subproject commit 1e41e0b70fdc536d2af6d7c5fb0934636c1026bf diff --git a/flext-dbt-oracle b/flext-dbt-oracle index aa47e882..5487acc7 160000 --- a/flext-dbt-oracle +++ b/flext-dbt-oracle @@ -1 +1 @@ -Subproject commit aa47e882952853c4ad0f035a9019da6e3ce68242 +Subproject commit 5487acc758b911450a522f2b05cd7a6c0902c118 diff --git a/flext-dbt-oracle-wms b/flext-dbt-oracle-wms index cb1fad0e..40e2ba06 160000 --- a/flext-dbt-oracle-wms +++ b/flext-dbt-oracle-wms @@ -1 +1 @@ -Subproject commit cb1fad0e4d9779872d784835ebddeb3d0fb7bdc1 +Subproject commit 40e2ba068935cf776ebf4835e3902b5a123531a5 diff --git a/flext-grpc b/flext-grpc index a7a09fd6..154fde43 160000 --- a/flext-grpc +++ b/flext-grpc @@ -1 +1 @@ -Subproject commit a7a09fd69fa8e1fa563a956e48e8100e8ccb7df2 +Subproject commit 154fde4317be71e7249adad7a3566e549796ac8b diff --git a/flext-ldap b/flext-ldap index cc25b4ae..0a012d80 160000 --- a/flext-ldap +++ b/flext-ldap @@ -1 +1 @@ -Subproject commit cc25b4aeb8a636a327dd4a3f81885c9d79ea817c +Subproject commit 0a012d8089219eca7bee73701c06776d85f09b82 diff --git a/flext-ldif b/flext-ldif index d15a7535..1fdd29d9 160000 --- a/flext-ldif +++ b/flext-ldif @@ -1 +1 @@ -Subproject commit d15a7535c742947852a6d5c9dbf1f1df82a0c015 +Subproject commit 1fdd29d9fe2cc117673c36c963e7ae307912be4d diff --git a/flext-meltano b/flext-meltano index 8377e2d8..c29fa541 160000 --- a/flext-meltano +++ b/flext-meltano @@ -1 +1 @@ -Subproject commit 8377e2d89cc74334862b9e6507074d1c4b1c144f +Subproject commit c29fa5418edc7f31c3ef55283100f3607781d485 diff --git a/flext-observability b/flext-observability index 40fd24fa..80a14f37 160000 --- a/flext-observability +++ b/flext-observability @@ -1 +1 @@ -Subproject commit 40fd24fa516513716c163d7f26f671e61bd3d6ed +Subproject commit 80a14f37a52a95f9776f2072f021edbb9f090520 diff --git a/flext-oracle-oic b/flext-oracle-oic index e4997844..e4f09682 160000 --- a/flext-oracle-oic +++ b/flext-oracle-oic @@ -1 +1 @@ -Subproject commit e4997844fcfa978de5fe180560de1d867983de7e +Subproject commit e4f096828784de3071febc3ac566b0951164257f diff --git a/flext-oracle-wms b/flext-oracle-wms index 619dd476..d5070fc7 160000 --- a/flext-oracle-wms +++ b/flext-oracle-wms @@ -1 +1 @@ -Subproject commit 619dd47684f861f66428693182179e3239601cf1 +Subproject commit d5070fc7e770f9c633151491f3105c1ec810029a diff --git a/flext-plugin b/flext-plugin index f43058df..b186454c 160000 --- a/flext-plugin +++ b/flext-plugin @@ -1 +1 @@ -Subproject commit f43058df63d27981667413162813b5c574d81b36 +Subproject commit b186454c6d232d04f6186ace050bad2a9cd41f12 diff --git a/flext-quality b/flext-quality index d04cb537..f5ca03d2 160000 --- a/flext-quality +++ b/flext-quality @@ -1 +1 @@ -Subproject commit d04cb537b12869ef6bfc4d9e4edf2c5d55374599 +Subproject commit f5ca03d2a259f77da9692e62f4d0b59ea6812c92 diff --git a/flext-tap-ldap b/flext-tap-ldap index 10fd918d..061f99c0 160000 --- a/flext-tap-ldap +++ b/flext-tap-ldap @@ -1 +1 @@ -Subproject commit 10fd918d314354691ec72cae7b34c00e35cc4601 +Subproject commit 061f99c0d162a85ff98573fd05fd5fdfdda02c8b diff --git a/flext-tap-ldif b/flext-tap-ldif index c78472e7..227c0c7e 160000 --- a/flext-tap-ldif +++ b/flext-tap-ldif @@ -1 +1 @@ -Subproject commit c78472e7c08163df9a48c00bb0986e4ccf594097 +Subproject commit 227c0c7e199a75bcf39a195d7b730008a1e9c28c diff --git a/flext-tap-oracle b/flext-tap-oracle index 738703ac..c7321d3c 160000 --- a/flext-tap-oracle +++ b/flext-tap-oracle @@ -1 +1 @@ -Subproject commit 738703ac0d3c9ec86cbf74925007269f37a17464 +Subproject commit c7321d3c8cab55944fc7842d2e43768780652e82 diff --git a/flext-tap-oracle-oic b/flext-tap-oracle-oic index d5762a59..de53e428 160000 --- a/flext-tap-oracle-oic +++ b/flext-tap-oracle-oic @@ -1 +1 @@ -Subproject commit d5762a59593e65ba1efd6d8ff4995b74ff3b9022 +Subproject commit de53e428a8812da54f5b26545981b2a28eace821 diff --git a/flext-tap-oracle-wms b/flext-tap-oracle-wms index cb0b0f1e..910b482a 160000 --- a/flext-tap-oracle-wms +++ b/flext-tap-oracle-wms @@ -1 +1 @@ -Subproject commit cb0b0f1e0af7fc0a4945bd8f98de8648de31eaf3 +Subproject commit 910b482a1ed4b138bd3f5a51dd314995d8819bc6 diff --git a/flext-target-ldap b/flext-target-ldap index 84f265bd..356f2b3a 160000 --- a/flext-target-ldap +++ b/flext-target-ldap @@ -1 +1 @@ -Subproject commit 84f265bd38be1a82c36fddfae66de22c039fbdf4 +Subproject commit 356f2b3ae701717593662049681fa9943d3ed6c6 diff --git a/flext-target-ldif b/flext-target-ldif index 19fd9334..458fd5c4 160000 --- a/flext-target-ldif +++ b/flext-target-ldif @@ -1 +1 @@ -Subproject commit 19fd933481c80ce272243ff2739bfa8df08361c8 +Subproject commit 458fd5c4cb52154f4b80849827ab413e284fdc6f diff --git a/flext-target-oracle b/flext-target-oracle index 462c11a6..ed7d9fe4 160000 --- a/flext-target-oracle +++ b/flext-target-oracle @@ -1 +1 @@ -Subproject commit 462c11a6c61995c6e8626faeb264964d2181b183 +Subproject commit ed7d9fe42750735e1727c39be793fd5919ce7416 diff --git a/flext-target-oracle-oic b/flext-target-oracle-oic index c9ed42bc..df82f6de 160000 --- a/flext-target-oracle-oic +++ b/flext-target-oracle-oic @@ -1 +1 @@ -Subproject commit c9ed42bc29ff670ef3c79666ecb29a585933e85e +Subproject commit df82f6def3fb117c2f686c69f938f1d805124573 diff --git a/flext-target-oracle-wms b/flext-target-oracle-wms index e15210d6..728206e1 160000 --- a/flext-target-oracle-wms +++ b/flext-target-oracle-wms @@ -1 +1 @@ -Subproject commit e15210d688ab1e03ce4bad511e95be60b2d28f64 +Subproject commit 728206e1f0d28bfab4c327723a49f3ec0e11acc9 diff --git a/flext-web b/flext-web index 8cc6df48..57c0bda9 160000 --- a/flext-web +++ b/flext-web @@ -1 +1 @@ -Subproject commit 8cc6df488c1b1ebe22e311e9c74a7dbd5f7ecce4 +Subproject commit 57c0bda9758f331dba036424f48c4f6e8458307b diff --git a/scripts/github/pr_manager.py b/scripts/github/pr_manager.py new file mode 100644 index 00000000..f3a2e061 --- /dev/null +++ b/scripts/github/pr_manager.py @@ -0,0 +1,199 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import argparse +import json +import subprocess +from pathlib import Path + + +def _run_capture(command: list[str], cwd: Path) -> str: + result = subprocess.run( + command, + cwd=cwd, + capture_output=True, + text=True, + check=False, + ) + if result.returncode != 0: + detail = (result.stderr or result.stdout).strip() + raise RuntimeError( + f"command failed ({result.returncode}): {' '.join(command)}: {detail}" + ) + return result.stdout.strip() + + +def _run_stream(command: list[str], cwd: Path) -> int: + result = subprocess.run(command, cwd=cwd, check=False) + return result.returncode + + +def _current_branch(repo_root: Path) -> str: + return _run_capture(["git", "rev-parse", "--abbrev-ref", "HEAD"], repo_root) + + +def _open_pr_for_head(repo_root: Path, head: str) -> dict[str, object] | None: + raw = _run_capture( + [ + "gh", + "pr", + "list", + "--state", + "open", + "--head", + head, + "--json", + "number,title,state,baseRefName,headRefName,url,isDraft", + "--limit", + "1", + ], + repo_root, + ) + payload = json.loads(raw) + if not payload: + return None + first = payload[0] + if not isinstance(first, dict): + return None + return first + + +def _print_status(repo_root: Path, base: str, head: str) -> int: + pr = _open_pr_for_head(repo_root, head) + print(f"repo={repo_root}") + print(f"base={base}") + print(f"head={head}") + if pr is None: + print("status=no-open-pr") + return 0 + print("status=open") + print(f"pr_number={pr.get('number')}") + print(f"pr_title={pr.get('title')}") + print(f"pr_url={pr.get('url')}") + print(f"pr_state={pr.get('state')}") + print(f"pr_draft={pr.get('isDraft')}") + return 0 + + +def _selector(pr_number: str, head: str) -> str: + return pr_number if pr_number else head + + +def _create_pr( + repo_root: Path, + base: str, + head: str, + title: str, + body: str, + draft: int, +) -> int: + existing = _open_pr_for_head(repo_root, head) + if existing is not None: + print(f"status=already-open") + print(f"pr_url={existing.get('url')}") + return 0 + + command = [ + "gh", + "pr", + "create", + "--base", + base, + "--head", + head, + "--title", + title, + "--body", + body, + ] + if draft == 1: + command.append("--draft") + + created = _run_capture(command, repo_root) + print("status=created") + print(f"pr_url={created}") + return 0 + + +def _merge_pr( + repo_root: Path, + selector: str, + method: str, + auto: int, + delete_branch: int, +) -> int: + command = ["gh", "pr", "merge", selector] + merge_flag = { + "merge": "--merge", + "rebase": "--rebase", + "squash": "--squash", + }.get(method, "--squash") + command.append(merge_flag) + if auto == 1: + command.append("--auto") + if delete_branch == 1: + command.append("--delete-branch") + exit_code = _run_stream(command, repo_root) + if exit_code == 0: + print("status=merged") + return exit_code + + +def _parse_args() -> argparse.Namespace: + parser = argparse.ArgumentParser() + _ = parser.add_argument("--repo-root", type=Path, default=Path(".")) + _ = parser.add_argument( + "--action", + default="status", + choices=["status", "create", "view", "checks", "merge", "close"], + ) + _ = parser.add_argument("--base", default="main") + _ = parser.add_argument("--head", default="") + _ = parser.add_argument("--number", default="") + _ = parser.add_argument("--title", default="") + _ = parser.add_argument("--body", default="") + _ = parser.add_argument("--draft", type=int, default=0) + _ = parser.add_argument("--merge-method", default="squash") + _ = parser.add_argument("--auto", type=int, default=0) + _ = parser.add_argument("--delete-branch", type=int, default=0) + return parser.parse_args() + + +def main() -> int: + args = _parse_args() + repo_root = args.repo_root.resolve() + head = args.head or _current_branch(repo_root) + base = args.base + selector = _selector(args.number, head) + + if args.action == "status": + return _print_status(repo_root, base, head) + + if args.action == "create": + title = args.title or f"chore: sync {head}" + body = args.body or "Automated PR managed by scripts/github/pr_manager.py" + return _create_pr(repo_root, base, head, title, body, args.draft) + + if args.action == "view": + return _run_stream(["gh", "pr", "view", selector], repo_root) + + if args.action == "checks": + return _run_stream(["gh", "pr", "checks", selector], repo_root) + + if args.action == "merge": + return _merge_pr( + repo_root, + selector, + args.merge_method, + args.auto, + args.delete_branch, + ) + + if args.action == "close": + return _run_stream(["gh", "pr", "close", selector], repo_root) + + raise RuntimeError(f"unknown action: {args.action}") + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tests/scripts/github/test_pr_manager.py b/tests/scripts/github/test_pr_manager.py new file mode 100644 index 00000000..90e94a9f --- /dev/null +++ b/tests/scripts/github/test_pr_manager.py @@ -0,0 +1,80 @@ +from __future__ import annotations + +import importlib.util +import json +import sys +from pathlib import Path +from typing import Any + +import pytest + + +def _load_module(module_name: str, relative_path: str) -> Any: + module_path = Path(__file__).resolve().parents[3] / relative_path + spec = importlib.util.spec_from_file_location(module_name, module_path) + assert spec is not None + assert spec.loader is not None + module = importlib.util.module_from_spec(spec) + sys.modules[spec.name] = module + spec.loader.exec_module(module) + return module + + +def test_selector_prefers_number() -> None: + mod = _load_module("pr_manager_selector", "scripts/github/pr_manager.py") + assert mod._selector("123", "feature/branch") == "123" + assert mod._selector("", "feature/branch") == "feature/branch" + + +def test_status_reports_no_open_pr( + monkeypatch: pytest.MonkeyPatch, capsys: pytest.CaptureFixture[str] +) -> None: + mod = _load_module("pr_manager_status", "scripts/github/pr_manager.py") + + def _fake_capture(command: list[str], _cwd: Path) -> str: + if command[:3] == ["gh", "pr", "list"]: + return "[]" + raise AssertionError(command) + + monkeypatch.setattr(mod, "_run_capture", _fake_capture) + + exit_code = mod._print_status(Path("/tmp/repo"), "main", "0.11.0-dev") + output = capsys.readouterr().out + assert exit_code == 0 + assert "status=no-open-pr" in output + + +def test_create_skips_when_existing_open_pr( + monkeypatch: pytest.MonkeyPatch, capsys: pytest.CaptureFixture[str] +) -> None: + mod = _load_module("pr_manager_create_existing", "scripts/github/pr_manager.py") + + def _fake_open(_repo_root: Path, _head: str) -> dict[str, object] | None: + return {"url": "https://example.com/pr/1"} + + monkeypatch.setattr(mod, "_open_pr_for_head", _fake_open) + + exit_code = mod._create_pr( + Path("/tmp/repo"), + "main", + "0.11.0-dev", + "title", + "body", + 0, + ) + output = capsys.readouterr().out + assert exit_code == 0 + assert "status=already-open" in output + + +def test_open_pr_for_head_parses_payload(monkeypatch: pytest.MonkeyPatch) -> None: + mod = _load_module("pr_manager_open_payload", "scripts/github/pr_manager.py") + payload = [{"number": 5, "url": "https://example.com/pr/5"}] + + def _fake_capture(_command: list[str], _cwd: Path) -> str: + return json.dumps(payload) + + monkeypatch.setattr(mod, "_run_capture", _fake_capture) + pr = mod._open_pr_for_head(Path("/tmp/repo"), "0.11.0-dev") + assert pr is not None + assert pr.get("number") == 5 From cf58975d631a3485ea37d845db7dc07a4b801a84 Mon Sep 17 00:00:00 2001 From: Marlon Costa Date: Fri, 20 Feb 2026 13:06:30 -0300 Subject: [PATCH 03/34] chore(workspace): update propagated project pointers --- flext-api | 2 +- flext-ldif | 2 +- flext-tap-ldif | 2 +- flext-tap-oracle | 2 +- flext-target-ldap | 2 +- flext-target-ldif | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/flext-api b/flext-api index 228874ad..1e5105ff 160000 --- a/flext-api +++ b/flext-api @@ -1 +1 @@ -Subproject commit 228874ad5991dc17d24b39bc20aecf580cdb3b96 +Subproject commit 1e5105fff3f489eed230ffdbdeaae16922f4010e diff --git a/flext-ldif b/flext-ldif index 1fdd29d9..8b058fab 160000 --- a/flext-ldif +++ b/flext-ldif @@ -1 +1 @@ -Subproject commit 1fdd29d9fe2cc117673c36c963e7ae307912be4d +Subproject commit 8b058fab908366d0ab149affa353cf1242e624f6 diff --git a/flext-tap-ldif b/flext-tap-ldif index 227c0c7e..3ec322b6 160000 --- a/flext-tap-ldif +++ b/flext-tap-ldif @@ -1 +1 @@ -Subproject commit 227c0c7e199a75bcf39a195d7b730008a1e9c28c +Subproject commit 3ec322b68b268952c6144fbd956a72e99f987940 diff --git a/flext-tap-oracle b/flext-tap-oracle index c7321d3c..42ed2997 160000 --- a/flext-tap-oracle +++ b/flext-tap-oracle @@ -1 +1 @@ -Subproject commit c7321d3c8cab55944fc7842d2e43768780652e82 +Subproject commit 42ed2997273d8331a4826f4d782785fa83e44255 diff --git a/flext-target-ldap b/flext-target-ldap index 356f2b3a..70b4c92a 160000 --- a/flext-target-ldap +++ b/flext-target-ldap @@ -1 +1 @@ -Subproject commit 356f2b3ae701717593662049681fa9943d3ed6c6 +Subproject commit 70b4c92aebe59e0949ccad952c94565ef0c00e14 diff --git a/flext-target-ldif b/flext-target-ldif index 458fd5c4..972532d8 160000 --- a/flext-target-ldif +++ b/flext-target-ldif @@ -1 +1 @@ -Subproject commit 458fd5c4cb52154f4b80849827ab413e284fdc6f +Subproject commit 972532d8f4aaa3af32956d9588eb166850fd1978 From d616bbb1618ed1586c8e681e9922bb00abb50d10 Mon Sep 17 00:00:00 2001 From: Marlon Costa Date: Fri, 20 Feb 2026 13:19:05 -0300 Subject: [PATCH 04/34] chore: checkpoint pending 0.11.0-dev changes --- flext-core | 2 +- flext-ldif | 2 +- scripts/maintenance/_discover.py | 45 ++-------- scripts/release/shared.py | 82 +++++-------------- .../release/test_release_shared_and_run.py | 31 ++----- .../modernize_pyproject_tests.py} | 14 ++-- .../dependencies/sync_internal_deps_tests.py} | 2 +- .../scripts/github/sync_workflows_tests.py} | 2 +- .../scripts/maintenance/_discover_tests.py} | 2 +- .../scripts/release/release_scripts_tests.py} | 2 +- 10 files changed, 47 insertions(+), 137 deletions(-) rename tests/{scripts/dependencies/test_modernize_pyproject.py => unit/scripts/dependencies/modernize_pyproject_tests.py} (95%) rename tests/{scripts/dependencies/test_sync_internal_deps.py => unit/scripts/dependencies/sync_internal_deps_tests.py} (99%) rename tests/{scripts/github/test_sync_workflows.py => unit/scripts/github/sync_workflows_tests.py} (98%) rename tests/{scripts/maintenance/test_discover.py => unit/scripts/maintenance/_discover_tests.py} (96%) rename tests/{scripts/release/test_release_scripts.py => unit/scripts/release/release_scripts_tests.py} (95%) diff --git a/flext-core b/flext-core index 43855380..4f89b1a8 160000 --- a/flext-core +++ b/flext-core @@ -1 +1 @@ -Subproject commit 43855380f19f5cee1aaaa23fbefaada453648bd5 +Subproject commit 4f89b1a840a81d66dd484d4e3ebc8bc57359b6a8 diff --git a/flext-ldif b/flext-ldif index 8b058fab..72720886 160000 --- a/flext-ldif +++ b/flext-ldif @@ -1 +1 @@ -Subproject commit 8b058fab908366d0ab149affa353cf1242e624f6 +Subproject commit 72720886bc40c885d2f3370f9f6cfce0b2d0337e diff --git a/scripts/maintenance/_discover.py b/scripts/maintenance/_discover.py index 07fa9429..0b117449 100644 --- a/scripts/maintenance/_discover.py +++ b/scripts/maintenance/_discover.py @@ -4,49 +4,14 @@ import argparse import json -import re import sys -from dataclasses import dataclass from pathlib import Path +REPO_ROOT = Path(__file__).resolve().parents[2] +if str(REPO_ROOT) not in sys.path: + sys.path.insert(0, str(REPO_ROOT)) -@dataclass(frozen=True) -class ProjectInfo: - path: Path - name: str - kind: str - - -def _is_git_project(path: Path) -> bool: - return (path / ".git").exists() - - -def _submodule_names(workspace_root: Path) -> set[str]: - gitmodules = workspace_root / ".gitmodules" - if not gitmodules.exists(): - return set() - try: - content = gitmodules.read_text(encoding="utf-8") - except OSError: - return set() - return set(re.findall(r"^\s*path\s*=\s*(.+?)\s*$", content, re.MULTILINE)) - - -def _discover(workspace_root: Path) -> list[ProjectInfo]: - projects: list[ProjectInfo] = [] - submodules = _submodule_names(workspace_root) - for entry in sorted(workspace_root.iterdir(), key=lambda value: value.name): - if not entry.is_dir() or entry.name == "cmd" or entry.name.startswith("."): - continue - if not _is_git_project(entry): - continue - if not (entry / "Makefile").exists(): - continue - if not (entry / "pyproject.toml").exists() and not (entry / "go.mod").exists(): - continue - kind = "submodule" if entry.name in submodules else "external" - projects.append(ProjectInfo(path=entry, name=entry.name, kind=kind)) - return projects +from libs.discovery import discover_projects def main() -> int: @@ -60,7 +25,7 @@ def main() -> int: _ = parser.add_argument("--workspace-root", type=Path, default=Path.cwd()) args = parser.parse_args() - projects = _discover(args.workspace_root.resolve()) + projects = discover_projects(args.workspace_root.resolve()) if args.kind != "all": projects = [p for p in projects if p.kind == args.kind] diff --git a/scripts/release/shared.py b/scripts/release/shared.py index 5c1d5c81..a1abb23c 100644 --- a/scripts/release/shared.py +++ b/scripts/release/shared.py @@ -2,72 +2,40 @@ # Owner-Skill: .claude/skills/scripts-maintenance/SKILL.md from __future__ import annotations -import json import re -import shlex -import subprocess import sys -from dataclasses import dataclass from pathlib import Path +REPO_ROOT = Path(__file__).resolve().parents[2] +if str(REPO_ROOT) not in sys.path: + sys.path.insert(0, str(REPO_ROOT)) + +from libs.discovery import ProjectInfo +from libs.paths import workspace_root as _workspace_root +from libs.selection import resolve_projects as _resolve_projects +from libs.subprocess import run_capture as _run_capture +from libs.subprocess import run_checked as _run_checked + SEMVER_RE = re.compile( r"^(?P0|[1-9]\d*)\.(?P0|[1-9]\d*)\.(?P0|[1-9]\d*)$" ) -@dataclass(frozen=True) -class Project: - name: str - path: Path +Project = ProjectInfo def workspace_root(path: str | Path = ".") -> Path: - return Path(path).resolve() - - -def discover_projects(root: Path) -> list[Project]: - discover = root / "scripts" / "maintenance" / "_discover.py" - command = [ - sys.executable, - str(discover), - "--workspace-root", - str(root), - "--kind", - "all", - "--format", - "json", - ] - result = subprocess.run(command, capture_output=True, text=True, check=False) - if result.returncode != 0: - msg = (result.stderr or result.stdout).strip() - raise RuntimeError(f"project discovery failed: {msg}") - payload = json.loads(result.stdout) - projects: list[Project] = [] - for item in payload.get("projects", []): - if not isinstance(item, dict): - continue - name = item.get("name") - path_value = item.get("path") - if not isinstance(name, str) or not isinstance(path_value, str): - continue - projects.append(Project(name=name, path=Path(path_value).resolve())) - return sorted(projects, key=lambda project: project.name) + return _workspace_root(path) def resolve_projects(root: Path, names: list[str]) -> list[Project]: - projects = discover_projects(root) - if not names: - return projects - - by_name = {project.name: project for project in projects} - missing = [name for name in names if name not in by_name] - if missing: - missing_text = ", ".join(sorted(missing)) - raise RuntimeError(f"unknown release projects: {missing_text}") - - resolved = [by_name[name] for name in names] - return sorted(resolved, key=lambda project: project.name) + try: + return _resolve_projects(root, names) + except RuntimeError as exc: + raise RuntimeError( + str(exc).replace("unknown projects", "unknown release projects") + ) from exc def parse_semver(version: str) -> tuple[int, int, int]: @@ -93,18 +61,8 @@ def bump_version(current_version: str, bump: str) -> str: def run_checked(command: list[str], cwd: Path | None = None) -> None: - result = subprocess.run(command, cwd=cwd, check=False) - if result.returncode != 0: - cmd = shlex.join(command) - raise RuntimeError(f"command failed ({result.returncode}): {cmd}") + _run_checked(command, cwd=cwd) def run_capture(command: list[str], cwd: Path | None = None) -> str: - result = subprocess.run( - command, cwd=cwd, capture_output=True, text=True, check=False - ) - if result.returncode != 0: - cmd = shlex.join(command) - detail = (result.stderr or result.stdout).strip() - raise RuntimeError(f"command failed ({result.returncode}): {cmd}: {detail}") - return result.stdout.strip() + return _run_capture(command, cwd=cwd) diff --git a/tests/scripts/release/test_release_shared_and_run.py b/tests/scripts/release/test_release_shared_and_run.py index 9b7437f0..d448cf88 100644 --- a/tests/scripts/release/test_release_shared_and_run.py +++ b/tests/scripts/release/test_release_shared_and_run.py @@ -1,7 +1,6 @@ from __future__ import annotations import importlib.util -import json import sys from pathlib import Path from types import SimpleNamespace @@ -23,21 +22,14 @@ def _load_module(module_name: str, relative_path: str) -> Any: def test_resolve_projects_uses_auto_discovery(monkeypatch: pytest.MonkeyPatch) -> None: shared = _load_module("release_shared", "scripts/release/shared.py") - payload = { - "projects": [ - {"name": "flext-api", "path": "/tmp/ws/flext-api", "kind": "submodule"}, - { - "name": "external-tool", - "path": "/tmp/ws/external-tool", - "kind": "external", - }, - ] - } - def _fake_run(*_args: object, **_kwargs: object) -> SimpleNamespace: - return SimpleNamespace(returncode=0, stdout=json.dumps(payload), stderr="") + def _fake_resolve(_root: Path, _names: list[str]) -> list[SimpleNamespace]: + return [ + SimpleNamespace(name="external-tool"), + SimpleNamespace(name="flext-api"), + ] - monkeypatch.setattr(shared.subprocess, "run", _fake_run) + monkeypatch.setattr(shared, "_resolve_projects", _fake_resolve) projects = shared.resolve_projects(Path("/tmp/ws"), []) assert [project.name for project in projects] == ["external-tool", "flext-api"] @@ -45,16 +37,11 @@ def _fake_run(*_args: object, **_kwargs: object) -> SimpleNamespace: def test_resolve_projects_rejects_unknown(monkeypatch: pytest.MonkeyPatch) -> None: shared = _load_module("release_shared_unknown", "scripts/release/shared.py") - payload = { - "projects": [ - {"name": "flext-api", "path": "/tmp/ws/flext-api", "kind": "submodule"}, - ] - } - def _fake_run(*_args: object, **_kwargs: object) -> SimpleNamespace: - return SimpleNamespace(returncode=0, stdout=json.dumps(payload), stderr="") + def _fake_resolve(_root: Path, _names: list[str]) -> list[object]: + raise RuntimeError("unknown projects: missing-project") - monkeypatch.setattr(shared.subprocess, "run", _fake_run) + monkeypatch.setattr(shared, "_resolve_projects", _fake_resolve) with pytest.raises(RuntimeError, match="unknown release projects"): _ = shared.resolve_projects(Path("/tmp/ws"), ["missing-project"]) diff --git a/tests/scripts/dependencies/test_modernize_pyproject.py b/tests/unit/scripts/dependencies/modernize_pyproject_tests.py similarity index 95% rename from tests/scripts/dependencies/test_modernize_pyproject.py rename to tests/unit/scripts/dependencies/modernize_pyproject_tests.py index 44de7a95..7993b3f0 100644 --- a/tests/scripts/dependencies/test_modernize_pyproject.py +++ b/tests/unit/scripts/dependencies/modernize_pyproject_tests.py @@ -10,7 +10,7 @@ def load_module() -> Any: module_path = ( - Path(__file__).resolve().parents[3] + Path(__file__).resolve().parents[4] / "scripts" / "dependencies" / "modernize_pyproject.py" @@ -26,7 +26,7 @@ def load_module() -> Any: def write_pyproject(project_dir: Path, content: str) -> Path: pyproject = project_dir / "pyproject.toml" - pyproject.write_text(content, encoding="utf-8") + _ = pyproject.write_text(content, encoding="utf-8") return pyproject @@ -73,7 +73,7 @@ def test_audit_exit_codes_reflect_violations( project_dir = tmp_path / "pkg" project_dir.mkdir() - write_pyproject( + _ = write_pyproject( project_dir, """ [build-system] @@ -87,7 +87,7 @@ def test_audit_exit_codes_reflect_violations( + "\n", ) - write_pyproject( + _ = write_pyproject( tmp_path, """ [project] @@ -107,7 +107,7 @@ def test_audit_exit_codes_reflect_violations( ) assert mod.main() == 1 - write_pyproject( + _ = write_pyproject( project_dir, """ [build-system] @@ -135,7 +135,7 @@ def test_audit_exit_codes_reflect_violations( ) (project_dir / "src" / "pkg").mkdir(parents=True) - write_pyproject( + _ = write_pyproject( tmp_path, """ [project] @@ -202,7 +202,7 @@ def test_bandit_skips_are_loaded_from_root_ssot( project_dir = root_dir / "pkg" project_dir.mkdir(parents=True) - write_pyproject( + _ = write_pyproject( root_dir, """ [project] diff --git a/tests/scripts/dependencies/test_sync_internal_deps.py b/tests/unit/scripts/dependencies/sync_internal_deps_tests.py similarity index 99% rename from tests/scripts/dependencies/test_sync_internal_deps.py rename to tests/unit/scripts/dependencies/sync_internal_deps_tests.py index 7b94f98a..5be73a59 100644 --- a/tests/scripts/dependencies/test_sync_internal_deps.py +++ b/tests/unit/scripts/dependencies/sync_internal_deps_tests.py @@ -11,7 +11,7 @@ def load_module() -> Any: module_path = ( - Path(__file__).resolve().parents[3] + Path(__file__).resolve().parents[4] / "scripts" / "dependencies" / "sync_internal_deps.py" diff --git a/tests/scripts/github/test_sync_workflows.py b/tests/unit/scripts/github/sync_workflows_tests.py similarity index 98% rename from tests/scripts/github/test_sync_workflows.py rename to tests/unit/scripts/github/sync_workflows_tests.py index 283d05f2..66440f69 100644 --- a/tests/scripts/github/test_sync_workflows.py +++ b/tests/unit/scripts/github/sync_workflows_tests.py @@ -9,7 +9,7 @@ def load_module() -> Any: module_path = ( - Path(__file__).resolve().parents[3] / "scripts" / "github" / "sync_workflows.py" + Path(__file__).resolve().parents[4] / "scripts" / "github" / "sync_workflows.py" ) spec = importlib.util.spec_from_file_location("sync_workflows", module_path) assert spec is not None diff --git a/tests/scripts/maintenance/test_discover.py b/tests/unit/scripts/maintenance/_discover_tests.py similarity index 96% rename from tests/scripts/maintenance/test_discover.py rename to tests/unit/scripts/maintenance/_discover_tests.py index 12742690..fd1931b0 100644 --- a/tests/scripts/maintenance/test_discover.py +++ b/tests/unit/scripts/maintenance/_discover_tests.py @@ -12,7 +12,7 @@ def load_module() -> Any: module_path = ( - Path(__file__).resolve().parents[3] / "scripts" / "maintenance" / "_discover.py" + Path(__file__).resolve().parents[4] / "scripts" / "maintenance" / "_discover.py" ) spec = importlib.util.spec_from_file_location("_discover", module_path) assert spec is not None diff --git a/tests/scripts/release/test_release_scripts.py b/tests/unit/scripts/release/release_scripts_tests.py similarity index 95% rename from tests/scripts/release/test_release_scripts.py rename to tests/unit/scripts/release/release_scripts_tests.py index 904a0ada..14ea3660 100644 --- a/tests/scripts/release/test_release_scripts.py +++ b/tests/unit/scripts/release/release_scripts_tests.py @@ -8,7 +8,7 @@ def _load_module(relative_path: str, module_name: str) -> Any: - module_path = Path(__file__).resolve().parents[3] / relative_path + module_path = Path(__file__).resolve().parents[4] / relative_path spec = importlib.util.spec_from_file_location(module_name, module_path) assert spec is not None assert spec.loader is not None From a8de2925e0f61f2452ff6e8b63b7ea695f457c7d Mon Sep 17 00:00:00 2001 From: Marlon Costa Date: Fri, 20 Feb 2026 13:25:15 -0300 Subject: [PATCH 05/34] chore: checkpoint pending 0.11.0-dev changes --- .gitignore | 2 + flexcore | 2 +- flext-api | 2 +- flext-auth | 2 +- flext-cli | 2 +- flext-core | 2 +- flext-db-oracle | 2 +- flext-dbt-ldap | 2 +- flext-dbt-ldif | 2 +- flext-dbt-oracle | 2 +- flext-dbt-oracle-wms | 2 +- flext-grpc | 2 +- flext-ldap | 2 +- flext-ldif | 2 +- flext-meltano | 2 +- flext-observability | 2 +- flext-oracle-oic | 2 +- flext-oracle-wms | 2 +- flext-plugin | 2 +- flext-quality | 2 +- flext-tap-ldap | 2 +- flext-tap-ldif | 2 +- flext-tap-oracle | 2 +- flext-tap-oracle-oic | 2 +- flext-tap-oracle-wms | 2 +- flext-target-ldap | 2 +- flext-target-ldif | 2 +- flext-target-oracle | 2 +- flext-target-oracle-oic | 2 +- flext-target-oracle-wms | 2 +- flext-web | 2 +- libs/__init__.py | 10 +++++ libs/discovery.py | 44 +++++++++++++++++++ libs/git.py | 14 ++++++ libs/paths.py | 11 +++++ libs/reporting.py | 13 ++++++ libs/selection.py | 26 +++++++++++ libs/subprocess.py | 27 ++++++++++++ pyproject.toml | 1 + .../dependencies/modernize_pyproject_tests.py | 6 +++ 40 files changed, 184 insertions(+), 30 deletions(-) create mode 100644 libs/__init__.py create mode 100644 libs/discovery.py create mode 100644 libs/git.py create mode 100644 libs/paths.py create mode 100644 libs/reporting.py create mode 100644 libs/selection.py create mode 100644 libs/subprocess.py diff --git a/.gitignore b/.gitignore index f2221b5b..35821a7f 100644 --- a/.gitignore +++ b/.gitignore @@ -30,6 +30,7 @@ !src/ !tests/ !typings/ +!libs/ !.vscode/ !.claude/ @@ -93,6 +94,7 @@ !src/** !tests/** !typings/** +!libs/** !.vscode/** !.claude/** diff --git a/flexcore b/flexcore index 34eaebf7..6cc1db0c 160000 --- a/flexcore +++ b/flexcore @@ -1 +1 @@ -Subproject commit 34eaebf71e72a609e5d3a753e2d28a7865723f55 +Subproject commit 6cc1db0c8e6086c9480f9e14b6012d58986a2621 diff --git a/flext-api b/flext-api index 1e5105ff..cdf93487 160000 --- a/flext-api +++ b/flext-api @@ -1 +1 @@ -Subproject commit 1e5105fff3f489eed230ffdbdeaae16922f4010e +Subproject commit cdf93487ff2aac9bbe8af35d52b4d27db3341ae7 diff --git a/flext-auth b/flext-auth index bdbf86f3..816f16bf 160000 --- a/flext-auth +++ b/flext-auth @@ -1 +1 @@ -Subproject commit bdbf86f3434904004c225a36991f10bef332738d +Subproject commit 816f16bf35a915a8ca5448f6ebfa72908ff43613 diff --git a/flext-cli b/flext-cli index 82ccd5df..79727351 160000 --- a/flext-cli +++ b/flext-cli @@ -1 +1 @@ -Subproject commit 82ccd5dfa4bba93651ec6eba9525e372ea068a0f +Subproject commit 79727351fc048a65c28ca135c01ade185dcaf955 diff --git a/flext-core b/flext-core index 4f89b1a8..6c611680 160000 --- a/flext-core +++ b/flext-core @@ -1 +1 @@ -Subproject commit 4f89b1a840a81d66dd484d4e3ebc8bc57359b6a8 +Subproject commit 6c6116802367d384aae7db1f66db07c7ee25ab19 diff --git a/flext-db-oracle b/flext-db-oracle index af67e948..a5f3ae5c 160000 --- a/flext-db-oracle +++ b/flext-db-oracle @@ -1 +1 @@ -Subproject commit af67e9480e6f7026f9865faf8f2d73594f31aa37 +Subproject commit a5f3ae5cf38456847e85a522970fdadcc0600da7 diff --git a/flext-dbt-ldap b/flext-dbt-ldap index 0a7484bd..6c04060c 160000 --- a/flext-dbt-ldap +++ b/flext-dbt-ldap @@ -1 +1 @@ -Subproject commit 0a7484bdd002167973a5bbe87dc4e8d160f05e4a +Subproject commit 6c04060caca2c0ac5f31bffb8041e911abecac1b diff --git a/flext-dbt-ldif b/flext-dbt-ldif index 1e41e0b7..3b2938a7 160000 --- a/flext-dbt-ldif +++ b/flext-dbt-ldif @@ -1 +1 @@ -Subproject commit 1e41e0b70fdc536d2af6d7c5fb0934636c1026bf +Subproject commit 3b2938a7960abbbe9957520bd860fec253dcc848 diff --git a/flext-dbt-oracle b/flext-dbt-oracle index 5487acc7..fff27d42 160000 --- a/flext-dbt-oracle +++ b/flext-dbt-oracle @@ -1 +1 @@ -Subproject commit 5487acc758b911450a522f2b05cd7a6c0902c118 +Subproject commit fff27d42eaa03c844284d2e05013311b6907e473 diff --git a/flext-dbt-oracle-wms b/flext-dbt-oracle-wms index 40e2ba06..10a3cc16 160000 --- a/flext-dbt-oracle-wms +++ b/flext-dbt-oracle-wms @@ -1 +1 @@ -Subproject commit 40e2ba068935cf776ebf4835e3902b5a123531a5 +Subproject commit 10a3cc16d15682e485da50ce2445c009b882414a diff --git a/flext-grpc b/flext-grpc index 154fde43..3d6b593d 160000 --- a/flext-grpc +++ b/flext-grpc @@ -1 +1 @@ -Subproject commit 154fde4317be71e7249adad7a3566e549796ac8b +Subproject commit 3d6b593dad1cc7d3d5de0a3657540928ad20b33e diff --git a/flext-ldap b/flext-ldap index 0a012d80..7727856e 160000 --- a/flext-ldap +++ b/flext-ldap @@ -1 +1 @@ -Subproject commit 0a012d8089219eca7bee73701c06776d85f09b82 +Subproject commit 7727856e6489e8adb9a2baa757ac462a5e6957cb diff --git a/flext-ldif b/flext-ldif index 72720886..c6b8c64e 160000 --- a/flext-ldif +++ b/flext-ldif @@ -1 +1 @@ -Subproject commit 72720886bc40c885d2f3370f9f6cfce0b2d0337e +Subproject commit c6b8c64ee9c7c9d188c2d0a56ad9b3d2838c4c71 diff --git a/flext-meltano b/flext-meltano index c29fa541..45766569 160000 --- a/flext-meltano +++ b/flext-meltano @@ -1 +1 @@ -Subproject commit c29fa5418edc7f31c3ef55283100f3607781d485 +Subproject commit 457665690bbb21e37e7ff18559eaf4c0eb29fc1c diff --git a/flext-observability b/flext-observability index 80a14f37..361bac75 160000 --- a/flext-observability +++ b/flext-observability @@ -1 +1 @@ -Subproject commit 80a14f37a52a95f9776f2072f021edbb9f090520 +Subproject commit 361bac75b10c8c7dcccc816eb8adf8d7816f9eb1 diff --git a/flext-oracle-oic b/flext-oracle-oic index e4f09682..ef3085a4 160000 --- a/flext-oracle-oic +++ b/flext-oracle-oic @@ -1 +1 @@ -Subproject commit e4f096828784de3071febc3ac566b0951164257f +Subproject commit ef3085a460bcb5dabb4cb5d6b788d63c7a9849f8 diff --git a/flext-oracle-wms b/flext-oracle-wms index d5070fc7..69cd5b3e 160000 --- a/flext-oracle-wms +++ b/flext-oracle-wms @@ -1 +1 @@ -Subproject commit d5070fc7e770f9c633151491f3105c1ec810029a +Subproject commit 69cd5b3eb8f63afca86efdc36fde4fc44bfea9e2 diff --git a/flext-plugin b/flext-plugin index b186454c..ad97c2be 160000 --- a/flext-plugin +++ b/flext-plugin @@ -1 +1 @@ -Subproject commit b186454c6d232d04f6186ace050bad2a9cd41f12 +Subproject commit ad97c2be88b5a3b4d1f7473e83d8688b26ec281e diff --git a/flext-quality b/flext-quality index f5ca03d2..2f022246 160000 --- a/flext-quality +++ b/flext-quality @@ -1 +1 @@ -Subproject commit f5ca03d2a259f77da9692e62f4d0b59ea6812c92 +Subproject commit 2f022246b4f39d6fbe22a3704a1b4c0888277c68 diff --git a/flext-tap-ldap b/flext-tap-ldap index 061f99c0..d8178b9e 160000 --- a/flext-tap-ldap +++ b/flext-tap-ldap @@ -1 +1 @@ -Subproject commit 061f99c0d162a85ff98573fd05fd5fdfdda02c8b +Subproject commit d8178b9eb676bd4d9c2a2629cb3ead1ebc78cab6 diff --git a/flext-tap-ldif b/flext-tap-ldif index 3ec322b6..a357ed30 160000 --- a/flext-tap-ldif +++ b/flext-tap-ldif @@ -1 +1 @@ -Subproject commit 3ec322b68b268952c6144fbd956a72e99f987940 +Subproject commit a357ed30d06b9e7bfe1bb33b2d7ae2ed71799ca9 diff --git a/flext-tap-oracle b/flext-tap-oracle index 42ed2997..5c622720 160000 --- a/flext-tap-oracle +++ b/flext-tap-oracle @@ -1 +1 @@ -Subproject commit 42ed2997273d8331a4826f4d782785fa83e44255 +Subproject commit 5c6227200767fb7b0f0308f15d9e8b05d5556b58 diff --git a/flext-tap-oracle-oic b/flext-tap-oracle-oic index de53e428..8c8eed5f 160000 --- a/flext-tap-oracle-oic +++ b/flext-tap-oracle-oic @@ -1 +1 @@ -Subproject commit de53e428a8812da54f5b26545981b2a28eace821 +Subproject commit 8c8eed5f6a7f01d5d6856682f3af937263bf6fd2 diff --git a/flext-tap-oracle-wms b/flext-tap-oracle-wms index 910b482a..77afd792 160000 --- a/flext-tap-oracle-wms +++ b/flext-tap-oracle-wms @@ -1 +1 @@ -Subproject commit 910b482a1ed4b138bd3f5a51dd314995d8819bc6 +Subproject commit 77afd792e6afaa4fe7fff60d8d564f4fd174a9f1 diff --git a/flext-target-ldap b/flext-target-ldap index 70b4c92a..4788ddb6 160000 --- a/flext-target-ldap +++ b/flext-target-ldap @@ -1 +1 @@ -Subproject commit 70b4c92aebe59e0949ccad952c94565ef0c00e14 +Subproject commit 4788ddb6c9e03f05a60342e4fb0101e09b7b4dbe diff --git a/flext-target-ldif b/flext-target-ldif index 972532d8..4d9334dc 160000 --- a/flext-target-ldif +++ b/flext-target-ldif @@ -1 +1 @@ -Subproject commit 972532d8f4aaa3af32956d9588eb166850fd1978 +Subproject commit 4d9334dc9b1c89f1d1f70281a3d5dbb40fe27c8d diff --git a/flext-target-oracle b/flext-target-oracle index ed7d9fe4..d542a9f9 160000 --- a/flext-target-oracle +++ b/flext-target-oracle @@ -1 +1 @@ -Subproject commit ed7d9fe42750735e1727c39be793fd5919ce7416 +Subproject commit d542a9f9f50416449d6b5b657fdae532e9e6958e diff --git a/flext-target-oracle-oic b/flext-target-oracle-oic index df82f6de..e252d4f7 160000 --- a/flext-target-oracle-oic +++ b/flext-target-oracle-oic @@ -1 +1 @@ -Subproject commit df82f6def3fb117c2f686c69f938f1d805124573 +Subproject commit e252d4f732a437a1b1d5622d063a0acf15614ec5 diff --git a/flext-target-oracle-wms b/flext-target-oracle-wms index 728206e1..f21f957e 160000 --- a/flext-target-oracle-wms +++ b/flext-target-oracle-wms @@ -1 +1 @@ -Subproject commit 728206e1f0d28bfab4c327723a49f3ec0e11acc9 +Subproject commit f21f957ef9a9e5a885ca71a08601be756dbe1180 diff --git a/flext-web b/flext-web index 57c0bda9..c5005e1b 160000 --- a/flext-web +++ b/flext-web @@ -1 +1 @@ -Subproject commit 57c0bda9758f331dba036424f48c4f6e8458307b +Subproject commit c5005e1b7053ed244cbdc81146a8e58386745ad7 diff --git a/libs/__init__.py b/libs/__init__.py new file mode 100644 index 00000000..9eb54b1d --- /dev/null +++ b/libs/__init__.py @@ -0,0 +1,10 @@ +from __future__ import annotations + +__all__ = [ + "discovery", + "git", + "paths", + "reporting", + "selection", + "subprocess", +] diff --git a/libs/discovery.py b/libs/discovery.py new file mode 100644 index 00000000..85b31be6 --- /dev/null +++ b/libs/discovery.py @@ -0,0 +1,44 @@ +from __future__ import annotations + +import re +from dataclasses import dataclass +from pathlib import Path + + +@dataclass(frozen=True) +class ProjectInfo: + path: Path + name: str + kind: str + + +def _is_git_project(path: Path) -> bool: + return (path / ".git").exists() + + +def _submodule_names(workspace_root: Path) -> set[str]: + gitmodules = workspace_root / ".gitmodules" + if not gitmodules.exists(): + return set() + try: + content = gitmodules.read_text(encoding="utf-8") + except OSError: + return set() + return set(re.findall(r"^\s*path\s*=\s*(.+?)\s*$", content, re.MULTILINE)) + + +def discover_projects(workspace_root: Path) -> list[ProjectInfo]: + projects: list[ProjectInfo] = [] + submodules = _submodule_names(workspace_root) + for entry in sorted(workspace_root.iterdir(), key=lambda value: value.name): + if not entry.is_dir() or entry.name == "cmd" or entry.name.startswith("."): + continue + if not _is_git_project(entry): + continue + if not (entry / "Makefile").exists(): + continue + if not (entry / "pyproject.toml").exists() and not (entry / "go.mod").exists(): + continue + kind = "submodule" if entry.name in submodules else "external" + projects.append(ProjectInfo(path=entry, name=entry.name, kind=kind)) + return projects diff --git a/libs/git.py b/libs/git.py new file mode 100644 index 00000000..ff152f99 --- /dev/null +++ b/libs/git.py @@ -0,0 +1,14 @@ +from __future__ import annotations + +from pathlib import Path + +from libs.subprocess import run_capture + + +def current_branch(repo_root: Path) -> str: + return run_capture(["git", "rev-parse", "--abbrev-ref", "HEAD"], cwd=repo_root) + + +def tag_exists(repo_root: Path, tag: str) -> bool: + value = run_capture(["git", "tag", "-l", tag], cwd=repo_root) + return value.strip() == tag diff --git a/libs/paths.py b/libs/paths.py new file mode 100644 index 00000000..46bc4f25 --- /dev/null +++ b/libs/paths.py @@ -0,0 +1,11 @@ +from __future__ import annotations + +from pathlib import Path + + +def workspace_root(path: str | Path = ".") -> Path: + return Path(path).resolve() + + +def repo_root_from_script(script_file: str | Path) -> Path: + return Path(script_file).resolve().parents[1] diff --git a/libs/reporting.py b/libs/reporting.py new file mode 100644 index 00000000..a2f57a5a --- /dev/null +++ b/libs/reporting.py @@ -0,0 +1,13 @@ +from __future__ import annotations + +from pathlib import Path + + +def reports_root(workspace_root: Path) -> Path: + return workspace_root / ".reports" + + +def ensure_report_dir(workspace_root: Path, *parts: str) -> Path: + path = reports_root(workspace_root).joinpath(*parts) + path.mkdir(parents=True, exist_ok=True) + return path diff --git a/libs/selection.py b/libs/selection.py new file mode 100644 index 00000000..d44082f5 --- /dev/null +++ b/libs/selection.py @@ -0,0 +1,26 @@ +from __future__ import annotations + +from pathlib import Path + +from libs.discovery import ProjectInfo, discover_projects + + +def filter_projects(projects: list[ProjectInfo], kind: str) -> list[ProjectInfo]: + if kind == "all": + return list(projects) + return [project for project in projects if project.kind == kind] + + +def resolve_projects(workspace_root: Path, names: list[str]) -> list[ProjectInfo]: + projects = discover_projects(workspace_root) + if not names: + return sorted(projects, key=lambda project: project.name) + + by_name = {project.name: project for project in projects} + missing = [name for name in names if name not in by_name] + if missing: + missing_text = ", ".join(sorted(missing)) + raise RuntimeError(f"unknown projects: {missing_text}") + + resolved = [by_name[name] for name in names] + return sorted(resolved, key=lambda project: project.name) diff --git a/libs/subprocess.py b/libs/subprocess.py new file mode 100644 index 00000000..cec83ca2 --- /dev/null +++ b/libs/subprocess.py @@ -0,0 +1,27 @@ +from __future__ import annotations + +import shlex +import subprocess +from pathlib import Path + + +def run_checked(command: list[str], cwd: Path | None = None) -> None: + result = subprocess.run(command, cwd=cwd, check=False) + if result.returncode != 0: + cmd = shlex.join(command) + raise RuntimeError(f"command failed ({result.returncode}): {cmd}") + + +def run_capture(command: list[str], cwd: Path | None = None) -> str: + result = subprocess.run( + command, + cwd=cwd, + capture_output=True, + text=True, + check=False, + ) + if result.returncode != 0: + cmd = shlex.join(command) + detail = (result.stderr or result.stdout).strip() + raise RuntimeError(f"command failed ({result.returncode}): {cmd}: {detail}") + return result.stdout.strip() diff --git a/pyproject.toml b/pyproject.toml index 694225e5..f5e90275 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -529,6 +529,7 @@ types-tabulate = "^0.9.0.20241207" ] python_files = [ "*_test.py", + "*_tests.py", "test_*.py", ] python_functions = [ diff --git a/tests/unit/scripts/dependencies/modernize_pyproject_tests.py b/tests/unit/scripts/dependencies/modernize_pyproject_tests.py index 7993b3f0..31037a79 100644 --- a/tests/unit/scripts/dependencies/modernize_pyproject_tests.py +++ b/tests/unit/scripts/dependencies/modernize_pyproject_tests.py @@ -94,6 +94,9 @@ def test_audit_exit_codes_reflect_violations( name = "workspace" version = "0.1.0" +[tool.pytest.ini_options] +addopts = ["--strict-config", "--strict-markers", "--tb=short", "-p no:sugar", "-q", "-ra"] + [tool.bandit] skips = ["B404", "B603", "B607", "B105", "B608"] """.strip() @@ -209,6 +212,9 @@ def test_bandit_skips_are_loaded_from_root_ssot( name = "workspace" version = "0.1.0" +[tool.pytest.ini_options] +addopts = ["--strict-config", "--strict-markers", "--tb=short", "-p no:sugar", "-q", "-ra"] + [tool.bandit] skips = ["B105", "B999"] """.strip() From 1f638d9743517a8ff48b3a252448280590596d9b Mon Sep 17 00:00:00 2001 From: Marlon Costa Date: Fri, 20 Feb 2026 13:32:38 -0300 Subject: [PATCH 06/34] chore(workspace): update flext-ldif pointer --- flext-ldif | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flext-ldif b/flext-ldif index c6b8c64e..fd8cffdb 160000 --- a/flext-ldif +++ b/flext-ldif @@ -1 +1 @@ -Subproject commit c6b8c64ee9c7c9d188c2d0a56ad9b3d2838c4c71 +Subproject commit fd8cffdb972a2fa73ca1b3b60f111e3166e0a50d From 00969675eb9601c57a4bc426092a91f006d03bb2 Mon Sep 17 00:00:00 2001 From: Marlon Costa Date: Fri, 20 Feb 2026 13:39:22 -0300 Subject: [PATCH 07/34] chore: checkpoint pending 0.11.0-dev changes --- Makefile | 3 + base.mk | 5 +- flexcore | 2 +- flext-api | 2 +- flext-auth | 2 +- flext-cli | 2 +- flext-core | 2 +- flext-db-oracle | 2 +- flext-dbt-ldap | 2 +- flext-dbt-ldif | 2 +- flext-dbt-oracle | 2 +- flext-dbt-oracle-wms | 2 +- flext-grpc | 2 +- flext-ldap | 2 +- flext-ldif | 2 +- flext-meltano | 2 +- flext-observability | 2 +- flext-oracle-oic | 2 +- flext-oracle-wms | 2 +- flext-plugin | 2 +- flext-quality | 2 +- flext-tap-ldap | 2 +- flext-tap-ldif | 2 +- flext-tap-oracle | 2 +- flext-tap-oracle-oic | 2 +- flext-tap-oracle-wms | 2 +- flext-target-ldap | 2 +- flext-target-ldif | 2 +- flext-target-oracle | 2 +- flext-target-oracle-oic | 2 +- flext-target-oracle-wms | 2 +- flext-web | 2 +- scripts/core/skill_validate.py | 49 +++---------- scripts/core/stub_supply_chain.py | 18 +++-- scripts/dependencies/dependency_detection.py | 39 +++++----- scripts/github/pr_manager.py | 7 +- scripts/github/sync_workflows.py | 36 +++------ scripts/maintenance/enforce_python_version.py | 18 +++-- tests/scripts/github/test_pr_manager.py | 73 +++++++++++++++++++ .../unit/scripts/core/skill_validate_tests.py | 45 ++++++++++++ .../enforce_python_version_tests.py | 47 ++++++++++++ 41 files changed, 270 insertions(+), 130 deletions(-) create mode 100644 tests/unit/scripts/core/skill_validate_tests.py create mode 100644 tests/unit/scripts/maintenance/enforce_python_version_tests.py diff --git a/Makefile b/Makefile index 0552ae4d..8f340f3a 100644 --- a/Makefile +++ b/Makefile @@ -33,6 +33,7 @@ PR_DRAFT ?= 0 PR_MERGE_METHOD ?= squash PR_AUTO ?= 0 PR_DELETE_BRANCH ?= 0 +PR_CHECKS_STRICT ?= 0 Q := @ ifdef VERBOSE @@ -175,6 +176,7 @@ help: ## Show simple workspace verbs $(Q)echo " PR_BASE=main PR_HEAD= PR_NUMBER= PR_DRAFT=0|1" $(Q)echo " PR_TITLE='title' PR_BODY='body' PR_MERGE_METHOD=squash|merge|rebase" $(Q)echo " PR_AUTO=0|1 PR_DELETE_BRANCH=0|1" + $(Q)echo " PR_CHECKS_STRICT=0|1 checks action strict failure toggle" $(Q)echo " DEPS_REPORT=0 Skip dependency report after upgrade/typings" $(Q)echo "" $(Q)echo "Examples:" @@ -468,6 +470,7 @@ pr: ## Manage pull requests for selected projects --make-arg "PR_MERGE_METHOD=$(PR_MERGE_METHOD)" \ --make-arg "PR_AUTO=$(PR_AUTO)" \ --make-arg "PR_DELETE_BRANCH=$(PR_DELETE_BRANCH)" \ + --make-arg "PR_CHECKS_STRICT=$(PR_CHECKS_STRICT)" \ $(SELECTED_PROJECTS) security: ## Run all security checks in all projects diff --git a/base.mk b/base.mk index adc8531f..cf69373b 100644 --- a/base.mk +++ b/base.mk @@ -29,6 +29,7 @@ PR_DRAFT ?= 0 PR_MERGE_METHOD ?= squash PR_AUTO ?= 0 PR_DELETE_BRANCH ?= 0 +PR_CHECKS_STRICT ?= 0 PYTEST_REPORT_ARGS := -ra --durations=25 --durations-min=0.001 --tb=short PYTEST_DIAG_ARGS := -rA --durations=0 --tb=long --showlocals @@ -188,6 +189,7 @@ help: ## Show commands $(Q)echo " PR_BASE=main PR_HEAD= PR_NUMBER=" $(Q)echo " PR_TITLE='title' PR_BODY='body' PR_DRAFT=0|1" $(Q)echo " PR_MERGE_METHOD=squash|merge|rebase PR_AUTO=0|1 PR_DELETE_BRANCH=0|1" + $(Q)echo " PR_CHECKS_STRICT=0|1 (checks: fail command only when strict=1)" setup: ## Complete setup $(Q)if [ "$(CORE_STACK)" = "go" ]; then \ @@ -513,7 +515,8 @@ pr: ## Manage pull requests for this repository --draft "$(PR_DRAFT)" \ --merge-method "$(PR_MERGE_METHOD)" \ --auto "$(PR_AUTO)" \ - --delete-branch "$(PR_DELETE_BRANCH)" + --delete-branch "$(PR_DELETE_BRANCH)" \ + --checks-strict "$(PR_CHECKS_STRICT)" clean: ## Clean artifacts $(Q)if [ "$(CORE_STACK)" = "go" ]; then \ diff --git a/flexcore b/flexcore index 6cc1db0c..3c7434b3 160000 --- a/flexcore +++ b/flexcore @@ -1 +1 @@ -Subproject commit 6cc1db0c8e6086c9480f9e14b6012d58986a2621 +Subproject commit 3c7434b3337280a5c82f1881fc5134db61fed863 diff --git a/flext-api b/flext-api index cdf93487..a7078d7d 160000 --- a/flext-api +++ b/flext-api @@ -1 +1 @@ -Subproject commit cdf93487ff2aac9bbe8af35d52b4d27db3341ae7 +Subproject commit a7078d7ddb93c365f8729b23710379cd5e769d0c diff --git a/flext-auth b/flext-auth index 816f16bf..3a69c72c 160000 --- a/flext-auth +++ b/flext-auth @@ -1 +1 @@ -Subproject commit 816f16bf35a915a8ca5448f6ebfa72908ff43613 +Subproject commit 3a69c72cd7c39473006a6c296b462579970c490d diff --git a/flext-cli b/flext-cli index 79727351..a07e51d8 160000 --- a/flext-cli +++ b/flext-cli @@ -1 +1 @@ -Subproject commit 79727351fc048a65c28ca135c01ade185dcaf955 +Subproject commit a07e51d8f7a791a204af4db53e30ee4376ecda42 diff --git a/flext-core b/flext-core index 6c611680..ffe0f4ee 160000 --- a/flext-core +++ b/flext-core @@ -1 +1 @@ -Subproject commit 6c6116802367d384aae7db1f66db07c7ee25ab19 +Subproject commit ffe0f4eee6937a0596af739127ee55806b542cc6 diff --git a/flext-db-oracle b/flext-db-oracle index a5f3ae5c..e1ad2f9a 160000 --- a/flext-db-oracle +++ b/flext-db-oracle @@ -1 +1 @@ -Subproject commit a5f3ae5cf38456847e85a522970fdadcc0600da7 +Subproject commit e1ad2f9a2d6bb530bef96a8042ff889e3f287646 diff --git a/flext-dbt-ldap b/flext-dbt-ldap index 6c04060c..4887fb02 160000 --- a/flext-dbt-ldap +++ b/flext-dbt-ldap @@ -1 +1 @@ -Subproject commit 6c04060caca2c0ac5f31bffb8041e911abecac1b +Subproject commit 4887fb02f214faffeead37c74cc8003d9a10700b diff --git a/flext-dbt-ldif b/flext-dbt-ldif index 3b2938a7..fe5655c6 160000 --- a/flext-dbt-ldif +++ b/flext-dbt-ldif @@ -1 +1 @@ -Subproject commit 3b2938a7960abbbe9957520bd860fec253dcc848 +Subproject commit fe5655c642819c96932a13720d89e576eea625e0 diff --git a/flext-dbt-oracle b/flext-dbt-oracle index fff27d42..15b14ae5 160000 --- a/flext-dbt-oracle +++ b/flext-dbt-oracle @@ -1 +1 @@ -Subproject commit fff27d42eaa03c844284d2e05013311b6907e473 +Subproject commit 15b14ae532f7059690fd945e3ebbd803e023b43d diff --git a/flext-dbt-oracle-wms b/flext-dbt-oracle-wms index 10a3cc16..ef4cc247 160000 --- a/flext-dbt-oracle-wms +++ b/flext-dbt-oracle-wms @@ -1 +1 @@ -Subproject commit 10a3cc16d15682e485da50ce2445c009b882414a +Subproject commit ef4cc2471d25e0d691fb300c6bb35f6a037a848c diff --git a/flext-grpc b/flext-grpc index 3d6b593d..126a0dd3 160000 --- a/flext-grpc +++ b/flext-grpc @@ -1 +1 @@ -Subproject commit 3d6b593dad1cc7d3d5de0a3657540928ad20b33e +Subproject commit 126a0dd3ebf578cd56c1391d9979551b26e2ca8b diff --git a/flext-ldap b/flext-ldap index 7727856e..f99e9e3c 160000 --- a/flext-ldap +++ b/flext-ldap @@ -1 +1 @@ -Subproject commit 7727856e6489e8adb9a2baa757ac462a5e6957cb +Subproject commit f99e9e3c43a0fd8f746638c918d398ce8aaffdab diff --git a/flext-ldif b/flext-ldif index fd8cffdb..f8e80e40 160000 --- a/flext-ldif +++ b/flext-ldif @@ -1 +1 @@ -Subproject commit fd8cffdb972a2fa73ca1b3b60f111e3166e0a50d +Subproject commit f8e80e40ff73c47b7dce0b9df8f0d95505f47a64 diff --git a/flext-meltano b/flext-meltano index 45766569..7fadb374 160000 --- a/flext-meltano +++ b/flext-meltano @@ -1 +1 @@ -Subproject commit 457665690bbb21e37e7ff18559eaf4c0eb29fc1c +Subproject commit 7fadb37478b1d2d629f07093a0ddb4e1d8a98d98 diff --git a/flext-observability b/flext-observability index 361bac75..2d10a1f0 160000 --- a/flext-observability +++ b/flext-observability @@ -1 +1 @@ -Subproject commit 361bac75b10c8c7dcccc816eb8adf8d7816f9eb1 +Subproject commit 2d10a1f0e87502bf0c782e17bf37639272d967a9 diff --git a/flext-oracle-oic b/flext-oracle-oic index ef3085a4..cc2e1617 160000 --- a/flext-oracle-oic +++ b/flext-oracle-oic @@ -1 +1 @@ -Subproject commit ef3085a460bcb5dabb4cb5d6b788d63c7a9849f8 +Subproject commit cc2e161734520bae4420c0796e85bb6e7b991db4 diff --git a/flext-oracle-wms b/flext-oracle-wms index 69cd5b3e..93739180 160000 --- a/flext-oracle-wms +++ b/flext-oracle-wms @@ -1 +1 @@ -Subproject commit 69cd5b3eb8f63afca86efdc36fde4fc44bfea9e2 +Subproject commit 9373918086b265969b728fd2f35aecffb71a18ef diff --git a/flext-plugin b/flext-plugin index ad97c2be..32231095 160000 --- a/flext-plugin +++ b/flext-plugin @@ -1 +1 @@ -Subproject commit ad97c2be88b5a3b4d1f7473e83d8688b26ec281e +Subproject commit 32231095315848dfa18676b6f21791a39a7eabe6 diff --git a/flext-quality b/flext-quality index 2f022246..d4dde310 160000 --- a/flext-quality +++ b/flext-quality @@ -1 +1 @@ -Subproject commit 2f022246b4f39d6fbe22a3704a1b4c0888277c68 +Subproject commit d4dde3104bd714f3f46f3a5866663d7124917c57 diff --git a/flext-tap-ldap b/flext-tap-ldap index d8178b9e..8541c6c4 160000 --- a/flext-tap-ldap +++ b/flext-tap-ldap @@ -1 +1 @@ -Subproject commit d8178b9eb676bd4d9c2a2629cb3ead1ebc78cab6 +Subproject commit 8541c6c472aac1f3395e291d7d861b32068810f3 diff --git a/flext-tap-ldif b/flext-tap-ldif index a357ed30..b479b6c9 160000 --- a/flext-tap-ldif +++ b/flext-tap-ldif @@ -1 +1 @@ -Subproject commit a357ed30d06b9e7bfe1bb33b2d7ae2ed71799ca9 +Subproject commit b479b6c9fc6c96e73e754bf15c0b5ff6a65ea941 diff --git a/flext-tap-oracle b/flext-tap-oracle index 5c622720..0fd87692 160000 --- a/flext-tap-oracle +++ b/flext-tap-oracle @@ -1 +1 @@ -Subproject commit 5c6227200767fb7b0f0308f15d9e8b05d5556b58 +Subproject commit 0fd8769249bf3b7d4be3d8e379f4dd0f6b22540b diff --git a/flext-tap-oracle-oic b/flext-tap-oracle-oic index 8c8eed5f..e4e3ae6f 160000 --- a/flext-tap-oracle-oic +++ b/flext-tap-oracle-oic @@ -1 +1 @@ -Subproject commit 8c8eed5f6a7f01d5d6856682f3af937263bf6fd2 +Subproject commit e4e3ae6f26fb8ce351936536aaa082734bb5203c diff --git a/flext-tap-oracle-wms b/flext-tap-oracle-wms index 77afd792..c01b262c 160000 --- a/flext-tap-oracle-wms +++ b/flext-tap-oracle-wms @@ -1 +1 @@ -Subproject commit 77afd792e6afaa4fe7fff60d8d564f4fd174a9f1 +Subproject commit c01b262ca838c14decb92410d2bc523d6c3f3fd6 diff --git a/flext-target-ldap b/flext-target-ldap index 4788ddb6..45bcf0b8 160000 --- a/flext-target-ldap +++ b/flext-target-ldap @@ -1 +1 @@ -Subproject commit 4788ddb6c9e03f05a60342e4fb0101e09b7b4dbe +Subproject commit 45bcf0b8bb366faad7ed7090613e627b49828bab diff --git a/flext-target-ldif b/flext-target-ldif index 4d9334dc..a4181c28 160000 --- a/flext-target-ldif +++ b/flext-target-ldif @@ -1 +1 @@ -Subproject commit 4d9334dc9b1c89f1d1f70281a3d5dbb40fe27c8d +Subproject commit a4181c28995ea0fb3ccdb453c1f9d0f1ed4d5a20 diff --git a/flext-target-oracle b/flext-target-oracle index d542a9f9..bf05d77a 160000 --- a/flext-target-oracle +++ b/flext-target-oracle @@ -1 +1 @@ -Subproject commit d542a9f9f50416449d6b5b657fdae532e9e6958e +Subproject commit bf05d77a27fb4a3eac8b7e74996deee6968f7e37 diff --git a/flext-target-oracle-oic b/flext-target-oracle-oic index e252d4f7..efc7d135 160000 --- a/flext-target-oracle-oic +++ b/flext-target-oracle-oic @@ -1 +1 @@ -Subproject commit e252d4f732a437a1b1d5622d063a0acf15614ec5 +Subproject commit efc7d1354fdd4e355d4667b3f2c7cab031133221 diff --git a/flext-target-oracle-wms b/flext-target-oracle-wms index f21f957e..2ddd8438 160000 --- a/flext-target-oracle-wms +++ b/flext-target-oracle-wms @@ -1 +1 @@ -Subproject commit f21f957ef9a9e5a885ca71a08601be756dbe1180 +Subproject commit 2ddd8438953882740460a86d5c8953fe5e456419 diff --git a/flext-web b/flext-web index c5005e1b..3d3de188 160000 --- a/flext-web +++ b/flext-web @@ -1 +1 @@ -Subproject commit c5005e1b7053ed244cbdc81146a8e58386745ad7 +Subproject commit 3d3de188943e99ffffff382f53f36588ac068e06 diff --git a/scripts/core/skill_validate.py b/scripts/core/skill_validate.py index a603630e..661d6488 100644 --- a/scripts/core/skill_validate.py +++ b/scripts/core/skill_validate.py @@ -12,6 +12,11 @@ import time from pathlib import Path +if str(Path(__file__).resolve().parents[2]) not in sys.path: + sys.path.insert(0, str(Path(__file__).resolve().parents[2])) + +from libs.discovery import discover_projects as ssot_discover_projects + try: import yaml # type: ignore[import-untyped] except ImportError: @@ -198,43 +203,13 @@ def discover_projects(root: Path) -> dict[str, object]: "root": "."} """ - gitmodules = root / ".gitmodules" - gitmodules_text = "" - if gitmodules.exists(): - try: - gitmodules_text = gitmodules.read_text(encoding="utf-8") - except OSError as exc: - msg = f"Cannot read {gitmodules}" - raise SkillInfraError(msg) from exc - - flext_projects: list[str] = [] - for line in gitmodules_text.splitlines(): - stripped = line.strip() - if "path = flext-" not in stripped: - continue - name = stripped.split("path = ", 1)[-1].strip() - if not name: - continue - if (root / name / "pyproject.toml").is_file(): - flext_projects.append(name) - - external_projects: list[str] = [] - for child in sorted(root.iterdir(), key=lambda p: p.name): - if not child.is_dir(): - continue - name = child.name - pyproject = child / "pyproject.toml" - if not pyproject.is_file(): - continue - if f"path = {name}" in gitmodules_text: - continue - try: - pyproject_text = pyproject.read_text(encoding="utf-8") - except OSError as exc: - msg = f"Cannot read {pyproject}" - raise SkillInfraError(msg) from exc - if "flext-core" in pyproject_text or "flext_core" in pyproject_text: - external_projects.append(name) + discovered = ssot_discover_projects(root) + flext_projects = [ + project.name for project in discovered if project.kind == "submodule" + ] + external_projects = [ + project.name for project in discovered if project.kind == "external" + ] return { "flext": unique_sorted(flext_projects), diff --git a/scripts/core/stub_supply_chain.py b/scripts/core/stub_supply_chain.py index 1fd9024e..ba32a048 100644 --- a/scripts/core/stub_supply_chain.py +++ b/scripts/core/stub_supply_chain.py @@ -16,6 +16,11 @@ from dataclasses import dataclass from pathlib import Path +if str(Path(__file__).resolve().parents[2]) not in sys.path: + sys.path.insert(0, str(Path(__file__).resolve().parents[2])) + +from libs.selection import resolve_projects + MISSING_IMPORT_RE = re.compile(r"Cannot find module `([^`]+)` \[missing-import\]") MYPY_HINT_RE = re.compile(r'note: Hint: "python3 -m pip install ([^"]+)"') MYPY_STUB_RE = re.compile(r'Library stubs not installed for "([^"]+)"') @@ -65,13 +70,12 @@ def run_cmd( def discover_projects(root: Path) -> list[Path]: - projects: list[Path] = [] - for entry in sorted(root.iterdir()): - if not entry.is_dir(): - continue - if (entry / "pyproject.toml").exists() and (entry / "src").is_dir(): - projects.append(entry) - return projects + return [ + project.path + for project in resolve_projects(root, names=[]) + if (project.path / "pyproject.toml").exists() + and (project.path / "src").is_dir() + ] def load_pyproject(project_dir: Path) -> dict[str, object]: diff --git a/scripts/dependencies/dependency_detection.py b/scripts/dependencies/dependency_detection.py index 8d932fa2..a31bbf6e 100644 --- a/scripts/dependencies/dependency_detection.py +++ b/scripts/dependencies/dependency_detection.py @@ -12,12 +12,19 @@ from __future__ import annotations import json +import os import re import subprocess +import sys import tomllib from pathlib import Path from typing import Any +if str(Path(__file__).resolve().parents[2]) not in sys.path: + sys.path.insert(0, str(Path(__file__).resolve().parents[2])) + +from libs.selection import resolve_projects + # Mypy output patterns for typing library detection (aligned with stub_supply_chain) MYPY_HINT_RE = re.compile(r'note: Hint: "python3 -m pip install ([^"]+)"') MYPY_STUB_RE = re.compile(r'Library stubs not installed for "([^"]+)"') @@ -62,22 +69,16 @@ def discover_projects( workspace_root: Path, projects_filter: list[str] | None = None, ) -> list[Path]: - """Discover all Python projects under workspace (top-level dirs with pyproject.toml). - - Matches root Makefile / sync_dependencies: any dir with pyproject.toml, excluding SKIP_DIRS. - """ - projects: list[Path] = [] - for item in sorted(workspace_root.iterdir()): - if not item.is_dir(): - continue - if any(skip in item.name for skip in SKIP_DIRS): - continue - if not (item / "pyproject.toml").exists(): - continue - if projects_filter is not None and item.name not in projects_filter: - continue - projects.append(item) - return projects + projects = [ + project.path + for project in resolve_projects(workspace_root, names=[]) + if (project.path / "pyproject.toml").exists() + and not any(skip in project.name for skip in SKIP_DIRS) + ] + if projects_filter is not None: + filter_set = set(projects_filter) + projects = [path for path in projects if path.name in filter_set] + return sorted(projects) def run_deptry( @@ -146,7 +147,7 @@ def run_pip_check(workspace_root: Path, venv_bin: Path) -> tuple[list[str], int] capture_output=True, text=True, timeout=60, - env={**subprocess.os.environ, "VIRTUAL_ENV": str(venv_bin.parent)}, + env={**os.environ, "VIRTUAL_ENV": str(venv_bin.parent)}, ) out = (result.stdout or "").strip().splitlines() if result.stdout else [] return out, result.returncode @@ -219,9 +220,9 @@ def run_mypy_stub_hints( "--no-error-summary", ] env = { - **subprocess.os.environ, + **os.environ, "VIRTUAL_ENV": str(venv_bin.parent), - "PATH": f"{venv_bin}:{subprocess.os.environ.get('PATH', '')}", + "PATH": f"{venv_bin}:{os.environ.get('PATH', '')}", } result = subprocess.run( cmd, diff --git a/scripts/github/pr_manager.py b/scripts/github/pr_manager.py index f3a2e061..d1cb7f7b 100644 --- a/scripts/github/pr_manager.py +++ b/scripts/github/pr_manager.py @@ -156,6 +156,7 @@ def _parse_args() -> argparse.Namespace: _ = parser.add_argument("--merge-method", default="squash") _ = parser.add_argument("--auto", type=int, default=0) _ = parser.add_argument("--delete-branch", type=int, default=0) + _ = parser.add_argument("--checks-strict", type=int, default=0) return parser.parse_args() @@ -178,7 +179,11 @@ def main() -> int: return _run_stream(["gh", "pr", "view", selector], repo_root) if args.action == "checks": - return _run_stream(["gh", "pr", "checks", selector], repo_root) + exit_code = _run_stream(["gh", "pr", "checks", selector], repo_root) + if exit_code != 0 and args.checks_strict == 0: + print("status=checks-nonblocking") + return 0 + return exit_code if args.action == "merge": return _merge_pr( diff --git a/scripts/github/sync_workflows.py b/scripts/github/sync_workflows.py index b6ba7684..7bf1dcb7 100644 --- a/scripts/github/sync_workflows.py +++ b/scripts/github/sync_workflows.py @@ -7,7 +7,12 @@ import sys from dataclasses import dataclass from pathlib import Path -from subprocess import CalledProcessError, run + +REPO_ROOT = Path(__file__).resolve().parents[2] +if str(REPO_ROOT) not in sys.path: + sys.path.insert(0, str(REPO_ROOT)) + +from libs.selection import resolve_projects GENERATED_HEADER = "# Generated by scripts/github/sync_workflows.py - DO NOT EDIT\n" MANAGED_FILES = {"ci.yml"} @@ -22,33 +27,10 @@ class Operation: def _discover_projects(workspace_root: Path) -> list[tuple[str, Path]]: - discover_script = workspace_root / "scripts" / "maintenance" / "_discover.py" - command = [ - sys.executable, - str(discover_script), - "--workspace-root", - str(workspace_root), - "--kind", - "all", - "--format", - "json", + return [ + (project.name, project.path) + for project in resolve_projects(workspace_root, names=[]) ] - try: - result = run(command, check=True, capture_output=True, text=True) - except CalledProcessError as exc: - message = (exc.stderr or exc.stdout or str(exc)).strip() - raise RuntimeError(f"project discovery failed: {message}") from exc - payload = json.loads(result.stdout) - projects: list[tuple[str, Path]] = [] - for item in payload.get("projects", []): - if not isinstance(item, dict): - continue - name = item.get("name") - path_value = item.get("path") - if not isinstance(name, str) or not isinstance(path_value, str): - continue - projects.append((name, Path(path_value).resolve())) - return projects def _render_template(template_path: Path) -> str: diff --git a/scripts/maintenance/enforce_python_version.py b/scripts/maintenance/enforce_python_version.py index 9606966d..6de692b7 100644 --- a/scripts/maintenance/enforce_python_version.py +++ b/scripts/maintenance/enforce_python_version.py @@ -21,6 +21,11 @@ import sys from pathlib import Path +if str(Path(__file__).resolve().parents[2]) not in sys.path: + sys.path.insert(0, str(Path(__file__).resolve().parents[2])) + +from libs.selection import resolve_projects + ROOT = Path(__file__).resolve().parents[2] REQUIRED_MINOR = 13 PYTHON_VERSION_CONTENT = f"3.{REQUIRED_MINOR}\n" @@ -56,14 +61,11 @@ def _discover_projects(workspace_root: Path) -> list[Path]: - """Discover all flext-* projects with pyproject.toml.""" - projects: list[Path] = [] - for entry in sorted(workspace_root.iterdir(), key=lambda v: v.name): - if not entry.is_dir() or not entry.name.startswith("flext-"): - continue - if (entry / "pyproject.toml").exists(): - projects.append(entry) - return projects + return [ + project.path + for project in resolve_projects(workspace_root, names=[]) + if (project.path / "pyproject.toml").exists() + ] def _ensure_python_version_file( diff --git a/tests/scripts/github/test_pr_manager.py b/tests/scripts/github/test_pr_manager.py index 90e94a9f..d5d41fb2 100644 --- a/tests/scripts/github/test_pr_manager.py +++ b/tests/scripts/github/test_pr_manager.py @@ -78,3 +78,76 @@ def _fake_capture(_command: list[str], _cwd: Path) -> str: pr = mod._open_pr_for_head(Path("/tmp/repo"), "0.11.0-dev") assert pr is not None assert pr.get("number") == 5 + + +def test_checks_action_nonblocking_by_default( + monkeypatch: pytest.MonkeyPatch, capsys: pytest.CaptureFixture[str] +) -> None: + mod = _load_module("pr_manager_checks_nonblocking", "scripts/github/pr_manager.py") + + def _fake_current_branch(_repo_root: Path) -> str: + return "0.11.0-dev" + + def _fake_run_stream(_command: list[str], _cwd: Path) -> int: + return 8 + + monkeypatch.setattr(mod, "_current_branch", _fake_current_branch) + monkeypatch.setattr(mod, "_run_stream", _fake_run_stream) + monkeypatch.setattr( + mod, + "_parse_args", + lambda: mod.argparse.Namespace( + repo_root=Path("."), + action="checks", + base="main", + head="", + number="", + title="", + body="", + draft=0, + merge_method="squash", + auto=0, + delete_branch=0, + checks_strict=0, + ), + ) + + exit_code = mod.main() + output = capsys.readouterr().out + assert exit_code == 0 + assert "status=checks-nonblocking" in output + + +def test_checks_action_strict_mode_returns_failure( + monkeypatch: pytest.MonkeyPatch, +) -> None: + mod = _load_module("pr_manager_checks_strict", "scripts/github/pr_manager.py") + + def _fake_current_branch(_repo_root: Path) -> str: + return "0.11.0-dev" + + def _fake_run_stream(_command: list[str], _cwd: Path) -> int: + return 8 + + monkeypatch.setattr(mod, "_current_branch", _fake_current_branch) + monkeypatch.setattr(mod, "_run_stream", _fake_run_stream) + monkeypatch.setattr( + mod, + "_parse_args", + lambda: mod.argparse.Namespace( + repo_root=Path("."), + action="checks", + base="main", + head="", + number="", + title="", + body="", + draft=0, + merge_method="squash", + auto=0, + delete_branch=0, + checks_strict=1, + ), + ) + + assert mod.main() == 8 diff --git a/tests/unit/scripts/core/skill_validate_tests.py b/tests/unit/scripts/core/skill_validate_tests.py new file mode 100644 index 00000000..08561aed --- /dev/null +++ b/tests/unit/scripts/core/skill_validate_tests.py @@ -0,0 +1,45 @@ +from __future__ import annotations + +import importlib.util +import sys +from pathlib import Path +from typing import Any + + +def load_module() -> Any: + module_path = ( + Path(__file__).resolve().parents[4] / "scripts" / "core" / "skill_validate.py" + ) + spec = importlib.util.spec_from_file_location("skill_validate", module_path) + assert spec is not None + assert spec.loader is not None + module = importlib.util.module_from_spec(spec) + sys.modules[spec.name] = module + spec.loader.exec_module(module) + return module + + +def _create_project(root: Path, name: str) -> None: + project = root / name + _ = project.mkdir(parents=True) + _ = (project / ".git").mkdir() + _ = (project / "Makefile").write_text("all:\n\t@true\n", encoding="utf-8") + _ = (project / "pyproject.toml").write_text( + "[project]\nname='demo'\nversion='0.1.0'\n", encoding="utf-8" + ) + + +def test_discover_projects_uses_ssot_submodule_and_external(tmp_path: Path) -> None: + mod = load_module() + _create_project(tmp_path, "flext-core") + _create_project(tmp_path, "algar-oud-mig") + _ = (tmp_path / ".gitmodules").write_text( + '[submodule "flext-core"]\n\tpath = flext-core\n\turl = git@github.com:flext-sh/flext-core.git\n', + encoding="utf-8", + ) + + discovered = mod.discover_projects(tmp_path) + + assert discovered["flext"] == ["flext-core"] + assert discovered["external"] == ["algar-oud-mig"] + assert discovered["root"] == "." diff --git a/tests/unit/scripts/maintenance/enforce_python_version_tests.py b/tests/unit/scripts/maintenance/enforce_python_version_tests.py new file mode 100644 index 00000000..5095b1b7 --- /dev/null +++ b/tests/unit/scripts/maintenance/enforce_python_version_tests.py @@ -0,0 +1,47 @@ +from __future__ import annotations + +import importlib.util +import sys +from pathlib import Path +from typing import Any + + +def load_module() -> Any: + module_path = ( + Path(__file__).resolve().parents[4] + / "scripts" + / "maintenance" + / "enforce_python_version.py" + ) + spec = importlib.util.spec_from_file_location("enforce_python_version", module_path) + assert spec is not None + assert spec.loader is not None + module = importlib.util.module_from_spec(spec) + sys.modules[spec.name] = module + spec.loader.exec_module(module) + return module + + +def _create_project(root: Path, name: str) -> None: + project = root / name + _ = project.mkdir(parents=True) + _ = (project / ".git").mkdir() + _ = (project / "Makefile").write_text("all:\n\t@true\n", encoding="utf-8") + _ = (project / "pyproject.toml").write_text( + "[project]\nname='demo'\nversion='0.1.0'\n", encoding="utf-8" + ) + + +def test_discover_projects_includes_external_projects(tmp_path: Path) -> None: + mod = load_module() + _create_project(tmp_path, "flext-core") + _create_project(tmp_path, "algar-oud-mig") + _ = (tmp_path / ".gitmodules").write_text( + '[submodule "flext-core"]\n\tpath = flext-core\n\turl = git@github.com:flext-sh/flext-core.git\n', + encoding="utf-8", + ) + + projects = mod._discover_projects(tmp_path) + names = [project.name for project in projects] + + assert names == ["algar-oud-mig", "flext-core"] From 9c63c476be849f1635a44ab95556709b15b99106 Mon Sep 17 00:00:00 2001 From: Marlon Costa Date: Fri, 20 Feb 2026 13:43:13 -0300 Subject: [PATCH 08/34] chore(workspace): update flexcore pointer --- flexcore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flexcore b/flexcore index 3c7434b3..14f50568 160000 --- a/flexcore +++ b/flexcore @@ -1 +1 @@ -Subproject commit 3c7434b3337280a5c82f1881fc5134db61fed863 +Subproject commit 14f5056857ddf7a7d9ae5a658ccc4a52d513b8c5 From be9ad89ff0d157dfef4202ca745a0278f3e01c34 Mon Sep 17 00:00:00 2001 From: Marlon Costa Date: Fri, 20 Feb 2026 13:48:27 -0300 Subject: [PATCH 09/34] test(scripts): centralize remaining script tests under unit layout --- .../scripts/github/pr_manager_tests.py} | 2 +- .../scripts/release/release_shared_and_run_tests.py} | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) rename tests/{scripts/github/test_pr_manager.py => unit/scripts/github/pr_manager_tests.py} (98%) rename tests/{scripts/release/test_release_shared_and_run.py => unit/scripts/release/release_shared_and_run_tests.py} (96%) diff --git a/tests/scripts/github/test_pr_manager.py b/tests/unit/scripts/github/pr_manager_tests.py similarity index 98% rename from tests/scripts/github/test_pr_manager.py rename to tests/unit/scripts/github/pr_manager_tests.py index d5d41fb2..462e31ac 100644 --- a/tests/scripts/github/test_pr_manager.py +++ b/tests/unit/scripts/github/pr_manager_tests.py @@ -10,7 +10,7 @@ def _load_module(module_name: str, relative_path: str) -> Any: - module_path = Path(__file__).resolve().parents[3] / relative_path + module_path = Path(__file__).resolve().parents[4] / relative_path spec = importlib.util.spec_from_file_location(module_name, module_path) assert spec is not None assert spec.loader is not None diff --git a/tests/scripts/release/test_release_shared_and_run.py b/tests/unit/scripts/release/release_shared_and_run_tests.py similarity index 96% rename from tests/scripts/release/test_release_shared_and_run.py rename to tests/unit/scripts/release/release_shared_and_run_tests.py index d448cf88..bfc5292c 100644 --- a/tests/scripts/release/test_release_shared_and_run.py +++ b/tests/unit/scripts/release/release_shared_and_run_tests.py @@ -10,7 +10,7 @@ def _load_module(module_name: str, relative_path: str) -> Any: - module_path = Path(__file__).resolve().parents[3] / relative_path + module_path = Path(__file__).resolve().parents[4] / relative_path spec = importlib.util.spec_from_file_location(module_name, module_path) assert spec is not None assert spec.loader is not None From 9ca7374fc816f301126f9fd7f3bd5ca984e81612 Mon Sep 17 00:00:00 2001 From: Marlon Costa Date: Fri, 20 Feb 2026 13:52:19 -0300 Subject: [PATCH 10/34] chore(workspace): update flext-core pointer --- flext-core | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flext-core b/flext-core index ffe0f4ee..22daaf34 160000 --- a/flext-core +++ b/flext-core @@ -1 +1 @@ -Subproject commit ffe0f4eee6937a0596af739127ee55806b542cc6 +Subproject commit 22daaf349bb6ef4c7328f8ab2b4ede026cfe4d52 From b66cb63c4abb14d18fe189a7a394f88b2efc3d7e Mon Sep 17 00:00:00 2001 From: Marlon Costa Date: Fri, 20 Feb 2026 14:02:25 -0300 Subject: [PATCH 11/34] chore(workspace): update flext-core pointer --- flext-core | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flext-core b/flext-core index 22daaf34..5ee8f6b8 160000 --- a/flext-core +++ b/flext-core @@ -1 +1 @@ -Subproject commit 22daaf349bb6ef4c7328f8ab2b4ede026cfe4d52 +Subproject commit 5ee8f6b8771295e02919f74d17d22d212d6217dd From 0f7ffe989a4cbfd4f2bd8661b6ab1c82cee63bcc Mon Sep 17 00:00:00 2001 From: Marlon Costa Date: Fri, 20 Feb 2026 14:13:49 -0300 Subject: [PATCH 12/34] chore: checkpoint pending 0.11.0-dev changes --- Makefile | 21 +++++++++ base.mk | 5 ++- flexcore | 2 +- flext-api | 2 +- flext-auth | 2 +- flext-cli | 2 +- flext-core | 2 +- flext-db-oracle | 2 +- flext-dbt-ldap | 2 +- flext-dbt-ldif | 2 +- flext-dbt-oracle | 2 +- flext-dbt-oracle-wms | 2 +- flext-grpc | 2 +- flext-ldap | 2 +- flext-ldif | 2 +- flext-meltano | 2 +- flext-observability | 2 +- flext-oracle-oic | 2 +- flext-oracle-wms | 2 +- flext-plugin | 2 +- flext-quality | 2 +- flext-tap-ldap | 2 +- flext-tap-ldif | 2 +- flext-tap-oracle | 2 +- flext-tap-oracle-oic | 2 +- flext-tap-oracle-wms | 2 +- flext-target-ldap | 2 +- flext-target-ldif | 2 +- flext-target-oracle | 2 +- flext-target-oracle-oic | 2 +- flext-target-oracle-wms | 2 +- flext-web | 2 +- scripts/github/pr_manager.py | 43 +++++++++++++++++++ tests/unit/scripts/github/pr_manager_tests.py | 42 ++++++++++++++++++ 34 files changed, 140 insertions(+), 31 deletions(-) diff --git a/Makefile b/Makefile index 8f340f3a..8c582652 100644 --- a/Makefile +++ b/Makefile @@ -34,6 +34,8 @@ PR_MERGE_METHOD ?= squash PR_AUTO ?= 0 PR_DELETE_BRANCH ?= 0 PR_CHECKS_STRICT ?= 0 +PR_RELEASE_ON_MERGE ?= 1 +PR_INCLUDE_ROOT ?= 1 Q := @ ifdef VERBOSE @@ -177,6 +179,8 @@ help: ## Show simple workspace verbs $(Q)echo " PR_TITLE='title' PR_BODY='body' PR_MERGE_METHOD=squash|merge|rebase" $(Q)echo " PR_AUTO=0|1 PR_DELETE_BRANCH=0|1" $(Q)echo " PR_CHECKS_STRICT=0|1 checks action strict failure toggle" + $(Q)echo " PR_RELEASE_ON_MERGE=0|1 merge action: dispatch release workflow" + $(Q)echo " PR_INCLUDE_ROOT=0|1 include root repo in workspace PR automation" $(Q)echo " DEPS_REPORT=0 Skip dependency report after upgrade/typings" $(Q)echo "" $(Q)echo "Examples:" @@ -471,7 +475,24 @@ pr: ## Manage pull requests for selected projects --make-arg "PR_AUTO=$(PR_AUTO)" \ --make-arg "PR_DELETE_BRANCH=$(PR_DELETE_BRANCH)" \ --make-arg "PR_CHECKS_STRICT=$(PR_CHECKS_STRICT)" \ + --make-arg "PR_RELEASE_ON_MERGE=$(PR_RELEASE_ON_MERGE)" \ $(SELECTED_PROJECTS) + $(Q)if [ "$(PR_INCLUDE_ROOT)" = "1" ]; then \ + python scripts/github/pr_manager.py \ + --repo-root "$(CURDIR)" \ + --action "$(PR_ACTION)" \ + --base "$(PR_BASE)" \ + $(if $(PR_HEAD),--head "$(PR_HEAD)",) \ + $(if $(PR_NUMBER),--number "$(PR_NUMBER)",) \ + $(if $(PR_TITLE),--title "$(PR_TITLE)",) \ + $(if $(PR_BODY),--body "$(PR_BODY)",) \ + --draft "$(PR_DRAFT)" \ + --merge-method "$(PR_MERGE_METHOD)" \ + --auto "$(PR_AUTO)" \ + --delete-branch "$(PR_DELETE_BRANCH)" \ + --checks-strict "$(PR_CHECKS_STRICT)" \ + --release-on-merge "$(PR_RELEASE_ON_MERGE)"; \ + fi security: ## Run all security checks in all projects $(Q)$(ENSURE_NO_PROJECT_CONFLICT) diff --git a/base.mk b/base.mk index cf69373b..207abfe2 100644 --- a/base.mk +++ b/base.mk @@ -30,6 +30,7 @@ PR_MERGE_METHOD ?= squash PR_AUTO ?= 0 PR_DELETE_BRANCH ?= 0 PR_CHECKS_STRICT ?= 0 +PR_RELEASE_ON_MERGE ?= 1 PYTEST_REPORT_ARGS := -ra --durations=25 --durations-min=0.001 --tb=short PYTEST_DIAG_ARGS := -rA --durations=0 --tb=long --showlocals @@ -190,6 +191,7 @@ help: ## Show commands $(Q)echo " PR_TITLE='title' PR_BODY='body' PR_DRAFT=0|1" $(Q)echo " PR_MERGE_METHOD=squash|merge|rebase PR_AUTO=0|1 PR_DELETE_BRANCH=0|1" $(Q)echo " PR_CHECKS_STRICT=0|1 (checks: fail command only when strict=1)" + $(Q)echo " PR_RELEASE_ON_MERGE=0|1 (merge: dispatch release workflow when branch maps to semver)" setup: ## Complete setup $(Q)if [ "$(CORE_STACK)" = "go" ]; then \ @@ -516,7 +518,8 @@ pr: ## Manage pull requests for this repository --merge-method "$(PR_MERGE_METHOD)" \ --auto "$(PR_AUTO)" \ --delete-branch "$(PR_DELETE_BRANCH)" \ - --checks-strict "$(PR_CHECKS_STRICT)" + --checks-strict "$(PR_CHECKS_STRICT)" \ + --release-on-merge "$(PR_RELEASE_ON_MERGE)" clean: ## Clean artifacts $(Q)if [ "$(CORE_STACK)" = "go" ]; then \ diff --git a/flexcore b/flexcore index 14f50568..8b4bb69e 160000 --- a/flexcore +++ b/flexcore @@ -1 +1 @@ -Subproject commit 14f5056857ddf7a7d9ae5a658ccc4a52d513b8c5 +Subproject commit 8b4bb69e3c7156ddbac63740773c3601de37babb diff --git a/flext-api b/flext-api index a7078d7d..9bffe320 160000 --- a/flext-api +++ b/flext-api @@ -1 +1 @@ -Subproject commit a7078d7ddb93c365f8729b23710379cd5e769d0c +Subproject commit 9bffe320e1dd22720351845e223b8644fd7ff9d3 diff --git a/flext-auth b/flext-auth index 3a69c72c..f65962ba 160000 --- a/flext-auth +++ b/flext-auth @@ -1 +1 @@ -Subproject commit 3a69c72cd7c39473006a6c296b462579970c490d +Subproject commit f65962bac3f9785af172cae28bb0a91bd95c6a00 diff --git a/flext-cli b/flext-cli index a07e51d8..f5e601f7 160000 --- a/flext-cli +++ b/flext-cli @@ -1 +1 @@ -Subproject commit a07e51d8f7a791a204af4db53e30ee4376ecda42 +Subproject commit f5e601f7726a1cda1a274bad30ea9b8ce6112d4e diff --git a/flext-core b/flext-core index 5ee8f6b8..47df85f7 160000 --- a/flext-core +++ b/flext-core @@ -1 +1 @@ -Subproject commit 5ee8f6b8771295e02919f74d17d22d212d6217dd +Subproject commit 47df85f7455554fe9accb851b61ea3f06745aab3 diff --git a/flext-db-oracle b/flext-db-oracle index e1ad2f9a..64b65767 160000 --- a/flext-db-oracle +++ b/flext-db-oracle @@ -1 +1 @@ -Subproject commit e1ad2f9a2d6bb530bef96a8042ff889e3f287646 +Subproject commit 64b65767c8bf69be95e2b85a543040505c4b5de0 diff --git a/flext-dbt-ldap b/flext-dbt-ldap index 4887fb02..6cc7278a 160000 --- a/flext-dbt-ldap +++ b/flext-dbt-ldap @@ -1 +1 @@ -Subproject commit 4887fb02f214faffeead37c74cc8003d9a10700b +Subproject commit 6cc7278a81cdac42eb40b55c4a4317a58e67ea8a diff --git a/flext-dbt-ldif b/flext-dbt-ldif index fe5655c6..c76c1cfc 160000 --- a/flext-dbt-ldif +++ b/flext-dbt-ldif @@ -1 +1 @@ -Subproject commit fe5655c642819c96932a13720d89e576eea625e0 +Subproject commit c76c1cfc43b2f0c9b1e6ab858bcd1b3af6b0a9f3 diff --git a/flext-dbt-oracle b/flext-dbt-oracle index 15b14ae5..daee56e3 160000 --- a/flext-dbt-oracle +++ b/flext-dbt-oracle @@ -1 +1 @@ -Subproject commit 15b14ae532f7059690fd945e3ebbd803e023b43d +Subproject commit daee56e3e4f07924c6a54c05da64080f8e211084 diff --git a/flext-dbt-oracle-wms b/flext-dbt-oracle-wms index ef4cc247..7a563cd7 160000 --- a/flext-dbt-oracle-wms +++ b/flext-dbt-oracle-wms @@ -1 +1 @@ -Subproject commit ef4cc2471d25e0d691fb300c6bb35f6a037a848c +Subproject commit 7a563cd7b8b931ae989480a7e80fc612d9070bd6 diff --git a/flext-grpc b/flext-grpc index 126a0dd3..3d16bd2c 160000 --- a/flext-grpc +++ b/flext-grpc @@ -1 +1 @@ -Subproject commit 126a0dd3ebf578cd56c1391d9979551b26e2ca8b +Subproject commit 3d16bd2c15de148a63be064efd75f3592f95eb3f diff --git a/flext-ldap b/flext-ldap index f99e9e3c..e1ad478a 160000 --- a/flext-ldap +++ b/flext-ldap @@ -1 +1 @@ -Subproject commit f99e9e3c43a0fd8f746638c918d398ce8aaffdab +Subproject commit e1ad478aa77c47dececd3ed9b1b0adc45b04b7ec diff --git a/flext-ldif b/flext-ldif index f8e80e40..37ae6d88 160000 --- a/flext-ldif +++ b/flext-ldif @@ -1 +1 @@ -Subproject commit f8e80e40ff73c47b7dce0b9df8f0d95505f47a64 +Subproject commit 37ae6d888039c3a0b2baae65eb76e093a7adeee3 diff --git a/flext-meltano b/flext-meltano index 7fadb374..6b68ff7b 160000 --- a/flext-meltano +++ b/flext-meltano @@ -1 +1 @@ -Subproject commit 7fadb37478b1d2d629f07093a0ddb4e1d8a98d98 +Subproject commit 6b68ff7b0b9277e3e6c00e9b345b172874c50821 diff --git a/flext-observability b/flext-observability index 2d10a1f0..71a7f9a0 160000 --- a/flext-observability +++ b/flext-observability @@ -1 +1 @@ -Subproject commit 2d10a1f0e87502bf0c782e17bf37639272d967a9 +Subproject commit 71a7f9a0b2900362c2f5e79dfc96e31a249cb28d diff --git a/flext-oracle-oic b/flext-oracle-oic index cc2e1617..9c6db8e7 160000 --- a/flext-oracle-oic +++ b/flext-oracle-oic @@ -1 +1 @@ -Subproject commit cc2e161734520bae4420c0796e85bb6e7b991db4 +Subproject commit 9c6db8e733a7406bb86e684b9573fd8078268090 diff --git a/flext-oracle-wms b/flext-oracle-wms index 93739180..a3245e1c 160000 --- a/flext-oracle-wms +++ b/flext-oracle-wms @@ -1 +1 @@ -Subproject commit 9373918086b265969b728fd2f35aecffb71a18ef +Subproject commit a3245e1ce85fe19262e1ace453ced8724bc00acb diff --git a/flext-plugin b/flext-plugin index 32231095..470603c4 160000 --- a/flext-plugin +++ b/flext-plugin @@ -1 +1 @@ -Subproject commit 32231095315848dfa18676b6f21791a39a7eabe6 +Subproject commit 470603c44ec6d874d884e096c61fbd5b419c7963 diff --git a/flext-quality b/flext-quality index d4dde310..9c6f3768 160000 --- a/flext-quality +++ b/flext-quality @@ -1 +1 @@ -Subproject commit d4dde3104bd714f3f46f3a5866663d7124917c57 +Subproject commit 9c6f376853daa4caca31aed04af11a355f3d45d6 diff --git a/flext-tap-ldap b/flext-tap-ldap index 8541c6c4..9b2b125f 160000 --- a/flext-tap-ldap +++ b/flext-tap-ldap @@ -1 +1 @@ -Subproject commit 8541c6c472aac1f3395e291d7d861b32068810f3 +Subproject commit 9b2b125febb67eb4d40047b6b3c644f3992bb163 diff --git a/flext-tap-ldif b/flext-tap-ldif index b479b6c9..b401b8f2 160000 --- a/flext-tap-ldif +++ b/flext-tap-ldif @@ -1 +1 @@ -Subproject commit b479b6c9fc6c96e73e754bf15c0b5ff6a65ea941 +Subproject commit b401b8f2788a0eb0c98af8b4fe492a494d3c2c15 diff --git a/flext-tap-oracle b/flext-tap-oracle index 0fd87692..be84fec4 160000 --- a/flext-tap-oracle +++ b/flext-tap-oracle @@ -1 +1 @@ -Subproject commit 0fd8769249bf3b7d4be3d8e379f4dd0f6b22540b +Subproject commit be84fec47b3bfad3f7bf7358883a41693b4d5698 diff --git a/flext-tap-oracle-oic b/flext-tap-oracle-oic index e4e3ae6f..066d985e 160000 --- a/flext-tap-oracle-oic +++ b/flext-tap-oracle-oic @@ -1 +1 @@ -Subproject commit e4e3ae6f26fb8ce351936536aaa082734bb5203c +Subproject commit 066d985ea590ed01c4f3ee057c3b15a53fd4e7bd diff --git a/flext-tap-oracle-wms b/flext-tap-oracle-wms index c01b262c..fe870ac4 160000 --- a/flext-tap-oracle-wms +++ b/flext-tap-oracle-wms @@ -1 +1 @@ -Subproject commit c01b262ca838c14decb92410d2bc523d6c3f3fd6 +Subproject commit fe870ac42ae23c741713262a7f553a941cb37649 diff --git a/flext-target-ldap b/flext-target-ldap index 45bcf0b8..8d574440 160000 --- a/flext-target-ldap +++ b/flext-target-ldap @@ -1 +1 @@ -Subproject commit 45bcf0b8bb366faad7ed7090613e627b49828bab +Subproject commit 8d57444035ed0976c45da771341326e472f2ac42 diff --git a/flext-target-ldif b/flext-target-ldif index a4181c28..8469557a 160000 --- a/flext-target-ldif +++ b/flext-target-ldif @@ -1 +1 @@ -Subproject commit a4181c28995ea0fb3ccdb453c1f9d0f1ed4d5a20 +Subproject commit 8469557a02c5051c94c0aa94e0e95abd0186e835 diff --git a/flext-target-oracle b/flext-target-oracle index bf05d77a..b296682a 160000 --- a/flext-target-oracle +++ b/flext-target-oracle @@ -1 +1 @@ -Subproject commit bf05d77a27fb4a3eac8b7e74996deee6968f7e37 +Subproject commit b296682a8b7f99d30aa6728d277924e41905a758 diff --git a/flext-target-oracle-oic b/flext-target-oracle-oic index efc7d135..bf02686e 160000 --- a/flext-target-oracle-oic +++ b/flext-target-oracle-oic @@ -1 +1 @@ -Subproject commit efc7d1354fdd4e355d4667b3f2c7cab031133221 +Subproject commit bf02686e85a03071a59964d406820560f3b4c0f3 diff --git a/flext-target-oracle-wms b/flext-target-oracle-wms index 2ddd8438..aeb095d2 160000 --- a/flext-target-oracle-wms +++ b/flext-target-oracle-wms @@ -1 +1 @@ -Subproject commit 2ddd8438953882740460a86d5c8953fe5e456419 +Subproject commit aeb095d26d09a5fd662f291d9e6b73ccd58f122a diff --git a/flext-web b/flext-web index 3d3de188..ee84ee28 160000 --- a/flext-web +++ b/flext-web @@ -1 +1 @@ -Subproject commit 3d3de188943e99ffffff382f53f36588ac068e06 +Subproject commit ee84ee28ea5b1066f58193bf75ecd44efe7659c9 diff --git a/scripts/github/pr_manager.py b/scripts/github/pr_manager.py index d1cb7f7b..0c3c53ad 100644 --- a/scripts/github/pr_manager.py +++ b/scripts/github/pr_manager.py @@ -3,6 +3,7 @@ import argparse import json +import re import subprocess from pathlib import Path @@ -79,6 +80,41 @@ def _selector(pr_number: str, head: str) -> str: return pr_number if pr_number else head +def _release_tag_from_head(head: str) -> str | None: + version = head.removesuffix("-dev") + if re.fullmatch(r"\d+\.\d+\.\d+", version): + return f"v{version}" + match = re.fullmatch(r"release/(?P\d+\.\d+\.\d+)", head) + if match: + return f"v{match.group('version')}" + return None + + +def _is_workspace_release_repo(repo_root: Path) -> bool: + return (repo_root / ".github" / "workflows" / "release.yml").exists() + + +def _trigger_release_if_needed(repo_root: Path, head: str) -> None: + if not _is_workspace_release_repo(repo_root): + return + tag = _release_tag_from_head(head) + if tag is None: + return + + if _run_stream(["gh", "release", "view", tag], repo_root) == 0: + print(f"status=release-exists tag={tag}") + return + + run_code = _run_stream( + ["gh", "workflow", "run", "release.yml", "-f", f"tag={tag}"], + repo_root, + ) + if run_code == 0: + print(f"status=release-dispatched tag={tag}") + else: + print(f"status=release-dispatch-failed tag={tag} exit={run_code}") + + def _create_pr( repo_root: Path, base: str, @@ -118,9 +154,11 @@ def _create_pr( def _merge_pr( repo_root: Path, selector: str, + head: str, method: str, auto: int, delete_branch: int, + release_on_merge: int, ) -> int: command = ["gh", "pr", "merge", selector] merge_flag = { @@ -136,6 +174,8 @@ def _merge_pr( exit_code = _run_stream(command, repo_root) if exit_code == 0: print("status=merged") + if release_on_merge == 1: + _trigger_release_if_needed(repo_root, head) return exit_code @@ -157,6 +197,7 @@ def _parse_args() -> argparse.Namespace: _ = parser.add_argument("--auto", type=int, default=0) _ = parser.add_argument("--delete-branch", type=int, default=0) _ = parser.add_argument("--checks-strict", type=int, default=0) + _ = parser.add_argument("--release-on-merge", type=int, default=1) return parser.parse_args() @@ -189,9 +230,11 @@ def main() -> int: return _merge_pr( repo_root, selector, + head, args.merge_method, args.auto, args.delete_branch, + args.release_on_merge, ) if args.action == "close": diff --git a/tests/unit/scripts/github/pr_manager_tests.py b/tests/unit/scripts/github/pr_manager_tests.py index 462e31ac..b67c3a32 100644 --- a/tests/unit/scripts/github/pr_manager_tests.py +++ b/tests/unit/scripts/github/pr_manager_tests.py @@ -151,3 +151,45 @@ def _fake_run_stream(_command: list[str], _cwd: Path) -> int: ) assert mod.main() == 8 + + +def test_release_tag_from_head_patterns() -> None: + mod = _load_module("pr_manager_release_tag", "scripts/github/pr_manager.py") + assert mod._release_tag_from_head("0.11.0-dev") == "v0.11.0" + assert mod._release_tag_from_head("release/0.12.3") == "v0.12.3" + assert mod._release_tag_from_head("feature/x") is None + + +def test_merge_triggers_release_dispatch_when_workspace_repo( + monkeypatch: pytest.MonkeyPatch, tmp_path: Path, capsys: pytest.CaptureFixture[str] +) -> None: + mod = _load_module("pr_manager_merge_release", "scripts/github/pr_manager.py") + workflows = tmp_path / ".github" / "workflows" + _ = workflows.mkdir(parents=True) + _ = (workflows / "release.yml").write_text("name: release\n", encoding="utf-8") + + calls: list[list[str]] = [] + + def _fake_run_stream(command: list[str], _cwd: Path) -> int: + calls.append(command) + if command[:3] == ["gh", "release", "view"]: + return 1 + return 0 + + monkeypatch.setattr(mod, "_run_stream", _fake_run_stream) + + exit_code = mod._merge_pr( + repo_root=tmp_path, + selector="123", + head="0.11.0-dev", + method="squash", + auto=1, + delete_branch=0, + release_on_merge=1, + ) + + assert exit_code == 0 + assert calls[0] == ["gh", "pr", "merge", "123", "--squash", "--auto"] + assert calls[1] == ["gh", "release", "view", "v0.11.0"] + assert calls[2] == ["gh", "workflow", "run", "release.yml", "-f", "tag=v0.11.0"] + assert "status=release-dispatched tag=v0.11.0" in capsys.readouterr().out From 15670f83efd638c6dd899d6fe474d43ce61b13d3 Mon Sep 17 00:00:00 2001 From: Marlon Costa Date: Fri, 20 Feb 2026 15:02:06 -0300 Subject: [PATCH 13/34] chore: checkpoint pending 0.11.0-dev changes --- Makefile | 53 +++---- flexcore | 2 +- flext-api | 2 +- flext-auth | 2 +- flext-cli | 2 +- flext-core | 2 +- flext-db-oracle | 2 +- flext-dbt-ldap | 2 +- flext-dbt-ldif | 2 +- flext-dbt-oracle | 2 +- flext-dbt-oracle-wms | 2 +- flext-grpc | 2 +- flext-ldap | 2 +- flext-ldif | 2 +- flext-meltano | 2 +- flext-observability | 2 +- flext-oracle-oic | 2 +- flext-oracle-wms | 2 +- flext-plugin | 2 +- flext-quality | 2 +- flext-tap-ldap | 2 +- flext-tap-ldif | 2 +- flext-tap-oracle | 2 +- flext-tap-oracle-oic | 2 +- flext-tap-oracle-wms | 2 +- flext-target-ldap | 2 +- flext-target-ldif | 2 +- flext-target-oracle | 2 +- flext-target-oracle-oic | 2 +- flext-target-oracle-wms | 2 +- flext-web | 2 +- scripts/github/pr_workspace.py | 179 ++++++++++++++++++++++ tests/scripts/github/test_pr_workspace.py | 150 ++++++++++++++++++ 33 files changed, 381 insertions(+), 61 deletions(-) create mode 100644 scripts/github/pr_workspace.py create mode 100644 tests/scripts/github/test_pr_workspace.py diff --git a/Makefile b/Makefile index 8c582652..e3acc1b2 100644 --- a/Makefile +++ b/Makefile @@ -36,6 +36,8 @@ PR_DELETE_BRANCH ?= 0 PR_CHECKS_STRICT ?= 0 PR_RELEASE_ON_MERGE ?= 1 PR_INCLUDE_ROOT ?= 1 +PR_BRANCH ?= 0.11.0-dev +PR_CHECKPOINT ?= 1 Q := @ ifdef VERBOSE @@ -181,6 +183,7 @@ help: ## Show simple workspace verbs $(Q)echo " PR_CHECKS_STRICT=0|1 checks action strict failure toggle" $(Q)echo " PR_RELEASE_ON_MERGE=0|1 merge action: dispatch release workflow" $(Q)echo " PR_INCLUDE_ROOT=0|1 include root repo in workspace PR automation" + $(Q)echo " PR_BRANCH= PR_CHECKPOINT=0|1 normalize branch + checkpoint before action" $(Q)echo " DEPS_REPORT=0 Skip dependency report after upgrade/typings" $(Q)echo "" $(Q)echo "Examples:" @@ -462,37 +465,25 @@ pr: ## Manage pull requests for selected projects $(Q)$(ENSURE_NO_PROJECT_CONFLICT) $(Q)$(ENSURE_SELECTED_PROJECTS) $(Q)$(ENSURE_PROJECTS_EXIST) - $(Q)$(ORCHESTRATOR) --verb pr \ - $(if $(filter 1,$(FAIL_FAST)),--fail-fast) \ - --make-arg "PR_ACTION=$(PR_ACTION)" \ - --make-arg "PR_BASE=$(PR_BASE)" \ - $(if $(PR_HEAD),--make-arg "PR_HEAD=$(PR_HEAD)",) \ - $(if $(PR_NUMBER),--make-arg "PR_NUMBER=$(PR_NUMBER)",) \ - $(if $(PR_TITLE),--make-arg "PR_TITLE=$(PR_TITLE)",) \ - $(if $(PR_BODY),--make-arg "PR_BODY=$(PR_BODY)",) \ - --make-arg "PR_DRAFT=$(PR_DRAFT)" \ - --make-arg "PR_MERGE_METHOD=$(PR_MERGE_METHOD)" \ - --make-arg "PR_AUTO=$(PR_AUTO)" \ - --make-arg "PR_DELETE_BRANCH=$(PR_DELETE_BRANCH)" \ - --make-arg "PR_CHECKS_STRICT=$(PR_CHECKS_STRICT)" \ - --make-arg "PR_RELEASE_ON_MERGE=$(PR_RELEASE_ON_MERGE)" \ - $(SELECTED_PROJECTS) - $(Q)if [ "$(PR_INCLUDE_ROOT)" = "1" ]; then \ - python scripts/github/pr_manager.py \ - --repo-root "$(CURDIR)" \ - --action "$(PR_ACTION)" \ - --base "$(PR_BASE)" \ - $(if $(PR_HEAD),--head "$(PR_HEAD)",) \ - $(if $(PR_NUMBER),--number "$(PR_NUMBER)",) \ - $(if $(PR_TITLE),--title "$(PR_TITLE)",) \ - $(if $(PR_BODY),--body "$(PR_BODY)",) \ - --draft "$(PR_DRAFT)" \ - --merge-method "$(PR_MERGE_METHOD)" \ - --auto "$(PR_AUTO)" \ - --delete-branch "$(PR_DELETE_BRANCH)" \ - --checks-strict "$(PR_CHECKS_STRICT)" \ - --release-on-merge "$(PR_RELEASE_ON_MERGE)"; \ - fi + $(Q)python scripts/github/pr_workspace.py \ + --workspace-root "$(CURDIR)" \ + $(foreach proj,$(SELECTED_PROJECTS),--project "$(proj)") \ + --include-root "$(PR_INCLUDE_ROOT)" \ + --branch "$(PR_BRANCH)" \ + --checkpoint "$(PR_CHECKPOINT)" \ + --fail-fast "$(if $(filter 1,$(FAIL_FAST)),1,0)" \ + --pr-action "$(PR_ACTION)" \ + --pr-base "$(PR_BASE)" \ + $(if $(PR_HEAD),--pr-head "$(PR_HEAD)",) \ + $(if $(PR_NUMBER),--pr-number "$(PR_NUMBER)",) \ + $(if $(PR_TITLE),--pr-title "$(PR_TITLE)",) \ + $(if $(PR_BODY),--pr-body "$(PR_BODY)",) \ + --pr-draft "$(PR_DRAFT)" \ + --pr-merge-method "$(PR_MERGE_METHOD)" \ + --pr-auto "$(PR_AUTO)" \ + --pr-delete-branch "$(PR_DELETE_BRANCH)" \ + --pr-checks-strict "$(PR_CHECKS_STRICT)" \ + --pr-release-on-merge "$(PR_RELEASE_ON_MERGE)" security: ## Run all security checks in all projects $(Q)$(ENSURE_NO_PROJECT_CONFLICT) diff --git a/flexcore b/flexcore index 8b4bb69e..52a0cd91 160000 --- a/flexcore +++ b/flexcore @@ -1 +1 @@ -Subproject commit 8b4bb69e3c7156ddbac63740773c3601de37babb +Subproject commit 52a0cd91460ce1a96e0d572594e481410f395af1 diff --git a/flext-api b/flext-api index 9bffe320..097d3ffc 160000 --- a/flext-api +++ b/flext-api @@ -1 +1 @@ -Subproject commit 9bffe320e1dd22720351845e223b8644fd7ff9d3 +Subproject commit 097d3ffcdcd8be2663f5135f794e317d82d0dfd1 diff --git a/flext-auth b/flext-auth index f65962ba..b7bae2a4 160000 --- a/flext-auth +++ b/flext-auth @@ -1 +1 @@ -Subproject commit f65962bac3f9785af172cae28bb0a91bd95c6a00 +Subproject commit b7bae2a4fb5c62d4b1a983622e6f4066d0ebc7f6 diff --git a/flext-cli b/flext-cli index f5e601f7..de6a49ee 160000 --- a/flext-cli +++ b/flext-cli @@ -1 +1 @@ -Subproject commit f5e601f7726a1cda1a274bad30ea9b8ce6112d4e +Subproject commit de6a49ee5bc9182585766b51ec1601c62e77881f diff --git a/flext-core b/flext-core index 47df85f7..0a8162da 160000 --- a/flext-core +++ b/flext-core @@ -1 +1 @@ -Subproject commit 47df85f7455554fe9accb851b61ea3f06745aab3 +Subproject commit 0a8162dabd7a00877a152496eca765af742c5107 diff --git a/flext-db-oracle b/flext-db-oracle index 64b65767..ee758d00 160000 --- a/flext-db-oracle +++ b/flext-db-oracle @@ -1 +1 @@ -Subproject commit 64b65767c8bf69be95e2b85a543040505c4b5de0 +Subproject commit ee758d00d1a024624a5e3560816c1d6851932ffb diff --git a/flext-dbt-ldap b/flext-dbt-ldap index 6cc7278a..84b63639 160000 --- a/flext-dbt-ldap +++ b/flext-dbt-ldap @@ -1 +1 @@ -Subproject commit 6cc7278a81cdac42eb40b55c4a4317a58e67ea8a +Subproject commit 84b63639725e3ac914057035063f66ea825dea56 diff --git a/flext-dbt-ldif b/flext-dbt-ldif index c76c1cfc..734a91f0 160000 --- a/flext-dbt-ldif +++ b/flext-dbt-ldif @@ -1 +1 @@ -Subproject commit c76c1cfc43b2f0c9b1e6ab858bcd1b3af6b0a9f3 +Subproject commit 734a91f07d81ea91ccddae9f6b3688d3eb99dbba diff --git a/flext-dbt-oracle b/flext-dbt-oracle index daee56e3..d45f6fb3 160000 --- a/flext-dbt-oracle +++ b/flext-dbt-oracle @@ -1 +1 @@ -Subproject commit daee56e3e4f07924c6a54c05da64080f8e211084 +Subproject commit d45f6fb370264ecde9e68ecc6bb3b5917d2ca0d3 diff --git a/flext-dbt-oracle-wms b/flext-dbt-oracle-wms index 7a563cd7..6db83b12 160000 --- a/flext-dbt-oracle-wms +++ b/flext-dbt-oracle-wms @@ -1 +1 @@ -Subproject commit 7a563cd7b8b931ae989480a7e80fc612d9070bd6 +Subproject commit 6db83b12add935727e4ed4948e8867fd69e3df75 diff --git a/flext-grpc b/flext-grpc index 3d16bd2c..2b8ca2f6 160000 --- a/flext-grpc +++ b/flext-grpc @@ -1 +1 @@ -Subproject commit 3d16bd2c15de148a63be064efd75f3592f95eb3f +Subproject commit 2b8ca2f6711a7e0ec6ef23ae43d6dc13fb3f40e6 diff --git a/flext-ldap b/flext-ldap index e1ad478a..fc8e6934 160000 --- a/flext-ldap +++ b/flext-ldap @@ -1 +1 @@ -Subproject commit e1ad478aa77c47dececd3ed9b1b0adc45b04b7ec +Subproject commit fc8e6934508179e5166e2706792c3d5fe05ae7e3 diff --git a/flext-ldif b/flext-ldif index 37ae6d88..46adc6d6 160000 --- a/flext-ldif +++ b/flext-ldif @@ -1 +1 @@ -Subproject commit 37ae6d888039c3a0b2baae65eb76e093a7adeee3 +Subproject commit 46adc6d62d9d2a08f32112c94128f09012857f1f diff --git a/flext-meltano b/flext-meltano index 6b68ff7b..7fadb374 160000 --- a/flext-meltano +++ b/flext-meltano @@ -1 +1 @@ -Subproject commit 6b68ff7b0b9277e3e6c00e9b345b172874c50821 +Subproject commit 7fadb37478b1d2d629f07093a0ddb4e1d8a98d98 diff --git a/flext-observability b/flext-observability index 71a7f9a0..2d10a1f0 160000 --- a/flext-observability +++ b/flext-observability @@ -1 +1 @@ -Subproject commit 71a7f9a0b2900362c2f5e79dfc96e31a249cb28d +Subproject commit 2d10a1f0e87502bf0c782e17bf37639272d967a9 diff --git a/flext-oracle-oic b/flext-oracle-oic index 9c6db8e7..cc2e1617 160000 --- a/flext-oracle-oic +++ b/flext-oracle-oic @@ -1 +1 @@ -Subproject commit 9c6db8e733a7406bb86e684b9573fd8078268090 +Subproject commit cc2e161734520bae4420c0796e85bb6e7b991db4 diff --git a/flext-oracle-wms b/flext-oracle-wms index a3245e1c..93739180 160000 --- a/flext-oracle-wms +++ b/flext-oracle-wms @@ -1 +1 @@ -Subproject commit a3245e1ce85fe19262e1ace453ced8724bc00acb +Subproject commit 9373918086b265969b728fd2f35aecffb71a18ef diff --git a/flext-plugin b/flext-plugin index 470603c4..32231095 160000 --- a/flext-plugin +++ b/flext-plugin @@ -1 +1 @@ -Subproject commit 470603c44ec6d874d884e096c61fbd5b419c7963 +Subproject commit 32231095315848dfa18676b6f21791a39a7eabe6 diff --git a/flext-quality b/flext-quality index 9c6f3768..d4dde310 160000 --- a/flext-quality +++ b/flext-quality @@ -1 +1 @@ -Subproject commit 9c6f376853daa4caca31aed04af11a355f3d45d6 +Subproject commit d4dde3104bd714f3f46f3a5866663d7124917c57 diff --git a/flext-tap-ldap b/flext-tap-ldap index 9b2b125f..8541c6c4 160000 --- a/flext-tap-ldap +++ b/flext-tap-ldap @@ -1 +1 @@ -Subproject commit 9b2b125febb67eb4d40047b6b3c644f3992bb163 +Subproject commit 8541c6c472aac1f3395e291d7d861b32068810f3 diff --git a/flext-tap-ldif b/flext-tap-ldif index b401b8f2..b479b6c9 160000 --- a/flext-tap-ldif +++ b/flext-tap-ldif @@ -1 +1 @@ -Subproject commit b401b8f2788a0eb0c98af8b4fe492a494d3c2c15 +Subproject commit b479b6c9fc6c96e73e754bf15c0b5ff6a65ea941 diff --git a/flext-tap-oracle b/flext-tap-oracle index be84fec4..0fd87692 160000 --- a/flext-tap-oracle +++ b/flext-tap-oracle @@ -1 +1 @@ -Subproject commit be84fec47b3bfad3f7bf7358883a41693b4d5698 +Subproject commit 0fd8769249bf3b7d4be3d8e379f4dd0f6b22540b diff --git a/flext-tap-oracle-oic b/flext-tap-oracle-oic index 066d985e..e4e3ae6f 160000 --- a/flext-tap-oracle-oic +++ b/flext-tap-oracle-oic @@ -1 +1 @@ -Subproject commit 066d985ea590ed01c4f3ee057c3b15a53fd4e7bd +Subproject commit e4e3ae6f26fb8ce351936536aaa082734bb5203c diff --git a/flext-tap-oracle-wms b/flext-tap-oracle-wms index fe870ac4..c01b262c 160000 --- a/flext-tap-oracle-wms +++ b/flext-tap-oracle-wms @@ -1 +1 @@ -Subproject commit fe870ac42ae23c741713262a7f553a941cb37649 +Subproject commit c01b262ca838c14decb92410d2bc523d6c3f3fd6 diff --git a/flext-target-ldap b/flext-target-ldap index 8d574440..45bcf0b8 160000 --- a/flext-target-ldap +++ b/flext-target-ldap @@ -1 +1 @@ -Subproject commit 8d57444035ed0976c45da771341326e472f2ac42 +Subproject commit 45bcf0b8bb366faad7ed7090613e627b49828bab diff --git a/flext-target-ldif b/flext-target-ldif index 8469557a..a4181c28 160000 --- a/flext-target-ldif +++ b/flext-target-ldif @@ -1 +1 @@ -Subproject commit 8469557a02c5051c94c0aa94e0e95abd0186e835 +Subproject commit a4181c28995ea0fb3ccdb453c1f9d0f1ed4d5a20 diff --git a/flext-target-oracle b/flext-target-oracle index b296682a..bf05d77a 160000 --- a/flext-target-oracle +++ b/flext-target-oracle @@ -1 +1 @@ -Subproject commit b296682a8b7f99d30aa6728d277924e41905a758 +Subproject commit bf05d77a27fb4a3eac8b7e74996deee6968f7e37 diff --git a/flext-target-oracle-oic b/flext-target-oracle-oic index bf02686e..efc7d135 160000 --- a/flext-target-oracle-oic +++ b/flext-target-oracle-oic @@ -1 +1 @@ -Subproject commit bf02686e85a03071a59964d406820560f3b4c0f3 +Subproject commit efc7d1354fdd4e355d4667b3f2c7cab031133221 diff --git a/flext-target-oracle-wms b/flext-target-oracle-wms index aeb095d2..2ddd8438 160000 --- a/flext-target-oracle-wms +++ b/flext-target-oracle-wms @@ -1 +1 @@ -Subproject commit aeb095d26d09a5fd662f291d9e6b73ccd58f122a +Subproject commit 2ddd8438953882740460a86d5c8953fe5e456419 diff --git a/flext-web b/flext-web index ee84ee28..3d3de188 160000 --- a/flext-web +++ b/flext-web @@ -1 +1 @@ -Subproject commit ee84ee28ea5b1066f58193bf75ecd44efe7659c9 +Subproject commit 3d3de188943e99ffffff382f53f36588ac068e06 diff --git a/scripts/github/pr_workspace.py b/scripts/github/pr_workspace.py new file mode 100644 index 00000000..1e000107 --- /dev/null +++ b/scripts/github/pr_workspace.py @@ -0,0 +1,179 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import argparse +import subprocess +import time +from pathlib import Path +import sys + + +REPO_ROOT = Path(__file__).resolve().parents[2] +if str(REPO_ROOT) not in sys.path: + sys.path.insert(0, str(REPO_ROOT)) + +from libs.selection import resolve_projects +from libs.subprocess import run_capture, run_checked + + +def _parse_args() -> argparse.Namespace: + parser = argparse.ArgumentParser() + _ = parser.add_argument("--workspace-root", type=Path, default=Path(".")) + _ = parser.add_argument("--project", action="append", default=[]) + _ = parser.add_argument("--include-root", type=int, default=1) + _ = parser.add_argument("--branch", default="") + _ = parser.add_argument("--fail-fast", type=int, default=0) + _ = parser.add_argument("--checkpoint", type=int, default=1) + _ = parser.add_argument("--pr-action", default="status") + _ = parser.add_argument("--pr-base", default="main") + _ = parser.add_argument("--pr-head", default="") + _ = parser.add_argument("--pr-number", default="") + _ = parser.add_argument("--pr-title", default="") + _ = parser.add_argument("--pr-body", default="") + _ = parser.add_argument("--pr-draft", default="0") + _ = parser.add_argument("--pr-merge-method", default="squash") + _ = parser.add_argument("--pr-auto", default="0") + _ = parser.add_argument("--pr-delete-branch", default="0") + _ = parser.add_argument("--pr-checks-strict", default="0") + _ = parser.add_argument("--pr-release-on-merge", default="1") + return parser.parse_args() + + +def _repo_display_name(repo_root: Path, workspace_root: Path) -> str: + return workspace_root.name if repo_root == workspace_root else repo_root.name + + +def _has_changes(repo_root: Path) -> bool: + return bool(run_capture(["git", "status", "--porcelain"], cwd=repo_root).strip()) + + +def _current_branch(repo_root: Path) -> str: + return run_capture(["git", "rev-parse", "--abbrev-ref", "HEAD"], cwd=repo_root) + + +def _checkout_branch(repo_root: Path, branch: str) -> None: + if not branch: + return + current = _current_branch(repo_root) + if current == branch: + return + checkout = subprocess.run( + ["git", "checkout", branch], + cwd=repo_root, + check=False, + capture_output=True, + text=True, + ) + if checkout.returncode == 0: + return + detail = (checkout.stderr or checkout.stdout).lower() + if "local changes" in detail or "would be overwritten" in detail: + run_checked(["git", "checkout", "-B", branch], cwd=repo_root) + return + fetch = subprocess.run( + ["git", "fetch", "origin", branch], + cwd=repo_root, + check=False, + capture_output=True, + text=True, + ) + if fetch.returncode == 0: + run_checked( + ["git", "checkout", "-B", branch, f"origin/{branch}"], cwd=repo_root + ) + else: + run_checked(["git", "checkout", "-B", branch], cwd=repo_root) + + +def _checkpoint(repo_root: Path, branch: str) -> None: + if not _has_changes(repo_root): + return + run_checked(["git", "add", "-A"], cwd=repo_root) + staged = run_capture(["git", "diff", "--cached", "--name-only"], cwd=repo_root) + if not staged.strip(): + return + run_checked( + ["git", "commit", "-m", "chore: checkpoint pending 0.11.0-dev changes"], + cwd=repo_root, + ) + push_cmd = ["git", "push", "-u", "origin", branch] if branch else ["git", "push"] + push = subprocess.run( + push_cmd, cwd=repo_root, check=False, capture_output=True, text=True + ) + if push.returncode == 0: + return + if branch: + run_checked(["git", "pull", "--rebase", "origin", branch], cwd=repo_root) + else: + run_checked(["git", "pull", "--rebase"], cwd=repo_root) + run_checked(push_cmd, cwd=repo_root) + + +def _run_pr(repo_root: Path, workspace_root: Path, args: argparse.Namespace) -> int: + report_dir = workspace_root / ".reports" / "workspace" / "pr" + report_dir.mkdir(parents=True, exist_ok=True) + display = _repo_display_name(repo_root, workspace_root) + log_path = report_dir / f"{display}.log" + command = [ + "make", + "-C", + str(repo_root), + "pr", + f"PR_ACTION={args.pr_action}", + f"PR_BASE={args.pr_base}", + f"PR_DRAFT={args.pr_draft}", + f"PR_MERGE_METHOD={args.pr_merge_method}", + f"PR_AUTO={args.pr_auto}", + f"PR_DELETE_BRANCH={args.pr_delete_branch}", + f"PR_CHECKS_STRICT={args.pr_checks_strict}", + f"PR_RELEASE_ON_MERGE={args.pr_release_on_merge}", + ] + if args.pr_head: + command.append(f"PR_HEAD={args.pr_head}") + if args.pr_number: + command.append(f"PR_NUMBER={args.pr_number}") + if args.pr_title: + command.append(f"PR_TITLE={args.pr_title}") + if args.pr_body: + command.append(f"PR_BODY={args.pr_body}") + + started = time.monotonic() + with log_path.open("w", encoding="utf-8") as handle: + result = subprocess.run( + command, stdout=handle, stderr=subprocess.STDOUT, check=False + ) + elapsed = int(time.monotonic() - started) + status = "OK" if result.returncode == 0 else "FAIL" + print( + f"[{status}] {display} pr ({elapsed}s) exit={result.returncode} log={log_path}" + ) + return result.returncode + + +def main() -> int: + args = _parse_args() + workspace_root = args.workspace_root.resolve() + projects = resolve_projects(workspace_root, list(args.project)) + repos = [project.path for project in projects] + if args.include_root == 1: + repos.append(workspace_root) + + failures = 0 + for repo_root in repos: + _checkout_branch(repo_root, args.branch) + if args.checkpoint == 1: + _checkpoint(repo_root, args.branch) + exit_code = _run_pr(repo_root, workspace_root, args) + if exit_code != 0: + failures += 1 + if args.fail_fast == 1: + break + + total = len(repos) + success = total - failures + print(f"summary total={total} success={success} fail={failures} skip=0") + return 1 if failures else 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tests/scripts/github/test_pr_workspace.py b/tests/scripts/github/test_pr_workspace.py new file mode 100644 index 00000000..7fdca469 --- /dev/null +++ b/tests/scripts/github/test_pr_workspace.py @@ -0,0 +1,150 @@ +from __future__ import annotations + +import importlib.util +import sys +from pathlib import Path +from types import SimpleNamespace +from typing import Any + +import pytest + + +def _load_module(module_name: str, relative_path: str) -> Any: + module_path = Path(__file__).resolve().parents[3] / relative_path + spec = importlib.util.spec_from_file_location(module_name, module_path) + assert spec is not None + assert spec.loader is not None + module = importlib.util.module_from_spec(spec) + sys.modules[spec.name] = module + spec.loader.exec_module(module) + return module + + +def test_main_runs_projects_and_root( + monkeypatch: pytest.MonkeyPatch, + tmp_path: Path, +) -> None: + mod = _load_module("pr_workspace_main", "scripts/github/pr_workspace.py") + + proj_a = tmp_path / "a" + proj_b = tmp_path / "b" + for path in (proj_a, proj_b, tmp_path): + _ = path.mkdir(parents=True, exist_ok=True) + _ = (path / ".git").mkdir(exist_ok=True) + + calls: list[tuple[str, Path]] = [] + + def _resolve_projects( + _workspace_root: Path, _names: list[str] + ) -> list[SimpleNamespace]: + return [ + SimpleNamespace(name="a", path=proj_a), + SimpleNamespace(name="b", path=proj_b), + ] + + def _checkout_branch(repo: Path, _branch: str) -> None: + calls.append(("co", repo)) + + def _checkpoint(repo: Path, _branch: str) -> None: + calls.append(("cp", repo)) + + def _run_pr(_repo: Path, _root: Path, _args: Any) -> int: + return 0 + + monkeypatch.setattr(mod, "resolve_projects", _resolve_projects) + monkeypatch.setattr(mod, "_checkout_branch", _checkout_branch) + monkeypatch.setattr(mod, "_checkpoint", _checkpoint) + monkeypatch.setattr(mod, "_run_pr", _run_pr) + monkeypatch.setattr( + mod, + "_parse_args", + lambda: mod.argparse.Namespace( + workspace_root=tmp_path, + project=[], + include_root=1, + branch="0.11.0-dev", + fail_fast=0, + checkpoint=1, + pr_action="status", + pr_base="main", + pr_head="", + pr_number="", + pr_title="", + pr_body="", + pr_draft="0", + pr_merge_method="squash", + pr_auto="0", + pr_delete_branch="0", + pr_checks_strict="0", + pr_release_on_merge="1", + ), + ) + + assert mod.main() == 0 + assert len([call for call in calls if call[0] == "co"]) == 3 + assert len([call for call in calls if call[0] == "cp"]) == 3 + + +def test_main_respects_fail_fast( + monkeypatch: pytest.MonkeyPatch, tmp_path: Path +) -> None: + mod = _load_module("pr_workspace_fail_fast", "scripts/github/pr_workspace.py") + + proj_a = tmp_path / "a" + proj_b = tmp_path / "b" + for path in (proj_a, proj_b): + _ = path.mkdir(parents=True, exist_ok=True) + _ = (path / ".git").mkdir(exist_ok=True) + + seen: list[Path] = [] + + def _resolve_projects( + _workspace_root: Path, _names: list[str] + ) -> list[SimpleNamespace]: + return [ + SimpleNamespace(name="a", path=proj_a), + SimpleNamespace(name="b", path=proj_b), + ] + + def _checkout_branch(_repo: Path, _branch: str) -> None: + return None + + def _checkpoint(_repo: Path, _branch: str) -> None: + return None + + monkeypatch.setattr(mod, "resolve_projects", _resolve_projects) + monkeypatch.setattr(mod, "_checkout_branch", _checkout_branch) + monkeypatch.setattr(mod, "_checkpoint", _checkpoint) + + def _run_pr(repo: Path, _root: Path, _args: Any) -> int: + seen.append(repo) + return 2 + + monkeypatch.setattr(mod, "_run_pr", _run_pr) + monkeypatch.setattr( + mod, + "_parse_args", + lambda: mod.argparse.Namespace( + workspace_root=tmp_path, + project=[], + include_root=0, + branch="0.11.0-dev", + fail_fast=1, + checkpoint=0, + pr_action="checks", + pr_base="main", + pr_head="", + pr_number="", + pr_title="", + pr_body="", + pr_draft="0", + pr_merge_method="squash", + pr_auto="0", + pr_delete_branch="0", + pr_checks_strict="1", + pr_release_on_merge="1", + ), + ) + + assert mod.main() == 1 + assert seen == [proj_a] From e45413fd43c2a2b61caf09af9c55296e80b072d6 Mon Sep 17 00:00:00 2001 From: Marlon Costa Date: Fri, 20 Feb 2026 15:03:11 -0300 Subject: [PATCH 14/34] chore(workspace): checkpoint subproject automation updates --- flext-db-oracle | 2 +- flext-dbt-ldap | 2 +- flext-dbt-ldif | 2 +- flext-dbt-oracle | 2 +- flext-dbt-oracle-wms | 2 +- flext-grpc | 2 +- flext-ldap | 2 +- flext-ldif | 2 +- flext-meltano | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/flext-db-oracle b/flext-db-oracle index ee758d00..0dbdaff6 160000 --- a/flext-db-oracle +++ b/flext-db-oracle @@ -1 +1 @@ -Subproject commit ee758d00d1a024624a5e3560816c1d6851932ffb +Subproject commit 0dbdaff6f50656bb12755387e40aef7451097de3 diff --git a/flext-dbt-ldap b/flext-dbt-ldap index 84b63639..0d98d169 160000 --- a/flext-dbt-ldap +++ b/flext-dbt-ldap @@ -1 +1 @@ -Subproject commit 84b63639725e3ac914057035063f66ea825dea56 +Subproject commit 0d98d169dd2d6dcd7b140a50474a160c69437968 diff --git a/flext-dbt-ldif b/flext-dbt-ldif index 734a91f0..9c12cbc0 160000 --- a/flext-dbt-ldif +++ b/flext-dbt-ldif @@ -1 +1 @@ -Subproject commit 734a91f07d81ea91ccddae9f6b3688d3eb99dbba +Subproject commit 9c12cbc02cdeb92ffec0c39373b16ec10cb82692 diff --git a/flext-dbt-oracle b/flext-dbt-oracle index d45f6fb3..21e5f763 160000 --- a/flext-dbt-oracle +++ b/flext-dbt-oracle @@ -1 +1 @@ -Subproject commit d45f6fb370264ecde9e68ecc6bb3b5917d2ca0d3 +Subproject commit 21e5f7637d4e05c1dbb8d296ceef19c516940cf4 diff --git a/flext-dbt-oracle-wms b/flext-dbt-oracle-wms index 6db83b12..c5611b0e 160000 --- a/flext-dbt-oracle-wms +++ b/flext-dbt-oracle-wms @@ -1 +1 @@ -Subproject commit 6db83b12add935727e4ed4948e8867fd69e3df75 +Subproject commit c5611b0e29bb14e45629f59b8fb61db3c832dc55 diff --git a/flext-grpc b/flext-grpc index 2b8ca2f6..27028cbb 160000 --- a/flext-grpc +++ b/flext-grpc @@ -1 +1 @@ -Subproject commit 2b8ca2f6711a7e0ec6ef23ae43d6dc13fb3f40e6 +Subproject commit 27028cbb9cd70d95736406db0e36bbe331ab5d23 diff --git a/flext-ldap b/flext-ldap index fc8e6934..dc43846f 160000 --- a/flext-ldap +++ b/flext-ldap @@ -1 +1 @@ -Subproject commit fc8e6934508179e5166e2706792c3d5fe05ae7e3 +Subproject commit dc43846fa314c01f526fd1c1963cbbcdac60d806 diff --git a/flext-ldif b/flext-ldif index 46adc6d6..d2ff3e25 160000 --- a/flext-ldif +++ b/flext-ldif @@ -1 +1 @@ -Subproject commit 46adc6d62d9d2a08f32112c94128f09012857f1f +Subproject commit d2ff3e2589ec230aff8516c430466f0518ba34d4 diff --git a/flext-meltano b/flext-meltano index 7fadb374..6b68ff7b 160000 --- a/flext-meltano +++ b/flext-meltano @@ -1 +1 @@ -Subproject commit 7fadb37478b1d2d629f07093a0ddb4e1d8a98d98 +Subproject commit 6b68ff7b0b9277e3e6c00e9b345b172874c50821 From e851e81a24e89d3b44e7fb3ce427b4f4514f19f0 Mon Sep 17 00:00:00 2001 From: Marlon Costa Date: Fri, 20 Feb 2026 15:03:50 -0300 Subject: [PATCH 15/34] chore(workspace): checkpoint remaining subproject updates --- flext-observability | 2 +- flext-oracle-oic | 2 +- flext-oracle-wms | 2 +- flext-plugin | 2 +- flext-quality | 2 +- flext-tap-ldap | 2 +- flext-tap-ldif | 2 +- flext-tap-oracle | 2 +- flext-tap-oracle-oic | 2 +- flext-tap-oracle-wms | 2 +- flext-target-ldap | 2 +- flext-target-ldif | 2 +- flext-target-oracle | 2 +- flext-target-oracle-oic | 2 +- flext-target-oracle-wms | 2 +- flext-web | 2 +- 16 files changed, 16 insertions(+), 16 deletions(-) diff --git a/flext-observability b/flext-observability index 2d10a1f0..71a7f9a0 160000 --- a/flext-observability +++ b/flext-observability @@ -1 +1 @@ -Subproject commit 2d10a1f0e87502bf0c782e17bf37639272d967a9 +Subproject commit 71a7f9a0b2900362c2f5e79dfc96e31a249cb28d diff --git a/flext-oracle-oic b/flext-oracle-oic index cc2e1617..9c6db8e7 160000 --- a/flext-oracle-oic +++ b/flext-oracle-oic @@ -1 +1 @@ -Subproject commit cc2e161734520bae4420c0796e85bb6e7b991db4 +Subproject commit 9c6db8e733a7406bb86e684b9573fd8078268090 diff --git a/flext-oracle-wms b/flext-oracle-wms index 93739180..a3245e1c 160000 --- a/flext-oracle-wms +++ b/flext-oracle-wms @@ -1 +1 @@ -Subproject commit 9373918086b265969b728fd2f35aecffb71a18ef +Subproject commit a3245e1ce85fe19262e1ace453ced8724bc00acb diff --git a/flext-plugin b/flext-plugin index 32231095..470603c4 160000 --- a/flext-plugin +++ b/flext-plugin @@ -1 +1 @@ -Subproject commit 32231095315848dfa18676b6f21791a39a7eabe6 +Subproject commit 470603c44ec6d874d884e096c61fbd5b419c7963 diff --git a/flext-quality b/flext-quality index d4dde310..9c6f3768 160000 --- a/flext-quality +++ b/flext-quality @@ -1 +1 @@ -Subproject commit d4dde3104bd714f3f46f3a5866663d7124917c57 +Subproject commit 9c6f376853daa4caca31aed04af11a355f3d45d6 diff --git a/flext-tap-ldap b/flext-tap-ldap index 8541c6c4..9b2b125f 160000 --- a/flext-tap-ldap +++ b/flext-tap-ldap @@ -1 +1 @@ -Subproject commit 8541c6c472aac1f3395e291d7d861b32068810f3 +Subproject commit 9b2b125febb67eb4d40047b6b3c644f3992bb163 diff --git a/flext-tap-ldif b/flext-tap-ldif index b479b6c9..b401b8f2 160000 --- a/flext-tap-ldif +++ b/flext-tap-ldif @@ -1 +1 @@ -Subproject commit b479b6c9fc6c96e73e754bf15c0b5ff6a65ea941 +Subproject commit b401b8f2788a0eb0c98af8b4fe492a494d3c2c15 diff --git a/flext-tap-oracle b/flext-tap-oracle index 0fd87692..be84fec4 160000 --- a/flext-tap-oracle +++ b/flext-tap-oracle @@ -1 +1 @@ -Subproject commit 0fd8769249bf3b7d4be3d8e379f4dd0f6b22540b +Subproject commit be84fec47b3bfad3f7bf7358883a41693b4d5698 diff --git a/flext-tap-oracle-oic b/flext-tap-oracle-oic index e4e3ae6f..066d985e 160000 --- a/flext-tap-oracle-oic +++ b/flext-tap-oracle-oic @@ -1 +1 @@ -Subproject commit e4e3ae6f26fb8ce351936536aaa082734bb5203c +Subproject commit 066d985ea590ed01c4f3ee057c3b15a53fd4e7bd diff --git a/flext-tap-oracle-wms b/flext-tap-oracle-wms index c01b262c..fe870ac4 160000 --- a/flext-tap-oracle-wms +++ b/flext-tap-oracle-wms @@ -1 +1 @@ -Subproject commit c01b262ca838c14decb92410d2bc523d6c3f3fd6 +Subproject commit fe870ac42ae23c741713262a7f553a941cb37649 diff --git a/flext-target-ldap b/flext-target-ldap index 45bcf0b8..8d574440 160000 --- a/flext-target-ldap +++ b/flext-target-ldap @@ -1 +1 @@ -Subproject commit 45bcf0b8bb366faad7ed7090613e627b49828bab +Subproject commit 8d57444035ed0976c45da771341326e472f2ac42 diff --git a/flext-target-ldif b/flext-target-ldif index a4181c28..8469557a 160000 --- a/flext-target-ldif +++ b/flext-target-ldif @@ -1 +1 @@ -Subproject commit a4181c28995ea0fb3ccdb453c1f9d0f1ed4d5a20 +Subproject commit 8469557a02c5051c94c0aa94e0e95abd0186e835 diff --git a/flext-target-oracle b/flext-target-oracle index bf05d77a..b296682a 160000 --- a/flext-target-oracle +++ b/flext-target-oracle @@ -1 +1 @@ -Subproject commit bf05d77a27fb4a3eac8b7e74996deee6968f7e37 +Subproject commit b296682a8b7f99d30aa6728d277924e41905a758 diff --git a/flext-target-oracle-oic b/flext-target-oracle-oic index efc7d135..bf02686e 160000 --- a/flext-target-oracle-oic +++ b/flext-target-oracle-oic @@ -1 +1 @@ -Subproject commit efc7d1354fdd4e355d4667b3f2c7cab031133221 +Subproject commit bf02686e85a03071a59964d406820560f3b4c0f3 diff --git a/flext-target-oracle-wms b/flext-target-oracle-wms index 2ddd8438..aeb095d2 160000 --- a/flext-target-oracle-wms +++ b/flext-target-oracle-wms @@ -1 +1 @@ -Subproject commit 2ddd8438953882740460a86d5c8953fe5e456419 +Subproject commit aeb095d26d09a5fd662f291d9e6b73ccd58f122a diff --git a/flext-web b/flext-web index 3d3de188..ee84ee28 160000 --- a/flext-web +++ b/flext-web @@ -1 +1 @@ -Subproject commit 3d3de188943e99ffffff382f53f36588ac068e06 +Subproject commit ee84ee28ea5b1066f58193bf75ecd44efe7659c9 From 9ba23bb0c4fa7afeb4003ea240c6b3fcf7a62677 Mon Sep 17 00:00:00 2001 From: Marlon Costa Date: Fri, 20 Feb 2026 15:05:51 -0300 Subject: [PATCH 16/34] chore: checkpoint pending 0.11.0-dev changes --- flexcore | 2 +- flext-api | 2 +- flext-auth | 2 +- flext-cli | 2 +- flext-core | 2 +- flext-db-oracle | 2 +- flext-dbt-ldap | 2 +- flext-dbt-ldif | 2 +- flext-dbt-oracle | 2 +- flext-dbt-oracle-wms | 2 +- flext-grpc | 2 +- flext-ldap | 2 +- flext-ldif | 2 +- flext-meltano | 2 +- flext-observability | 2 +- flext-oracle-oic | 2 +- 16 files changed, 16 insertions(+), 16 deletions(-) diff --git a/flexcore b/flexcore index 52a0cd91..e92413e4 160000 --- a/flexcore +++ b/flexcore @@ -1 +1 @@ -Subproject commit 52a0cd91460ce1a96e0d572594e481410f395af1 +Subproject commit e92413e435e9f8977bca145ec4e68cf21e6c87e6 diff --git a/flext-api b/flext-api index 097d3ffc..7ff470c2 160000 --- a/flext-api +++ b/flext-api @@ -1 +1 @@ -Subproject commit 097d3ffcdcd8be2663f5135f794e317d82d0dfd1 +Subproject commit 7ff470c220d5ba4a8a7a67e10643060aa80ea21b diff --git a/flext-auth b/flext-auth index b7bae2a4..028f61c1 160000 --- a/flext-auth +++ b/flext-auth @@ -1 +1 @@ -Subproject commit b7bae2a4fb5c62d4b1a983622e6f4066d0ebc7f6 +Subproject commit 028f61c13ce4055cedfa31de82c1388f7d68124b diff --git a/flext-cli b/flext-cli index de6a49ee..679dd76b 160000 --- a/flext-cli +++ b/flext-cli @@ -1 +1 @@ -Subproject commit de6a49ee5bc9182585766b51ec1601c62e77881f +Subproject commit 679dd76b668e37d94ee5c5091f2a067632a2c7b5 diff --git a/flext-core b/flext-core index 0a8162da..4c68d58a 160000 --- a/flext-core +++ b/flext-core @@ -1 +1 @@ -Subproject commit 0a8162dabd7a00877a152496eca765af742c5107 +Subproject commit 4c68d58aa5107677f7eefbdb828be867258805eb diff --git a/flext-db-oracle b/flext-db-oracle index 0dbdaff6..2ba7f106 160000 --- a/flext-db-oracle +++ b/flext-db-oracle @@ -1 +1 @@ -Subproject commit 0dbdaff6f50656bb12755387e40aef7451097de3 +Subproject commit 2ba7f106fbea482606e8a2252d9d73dcd2695cf3 diff --git a/flext-dbt-ldap b/flext-dbt-ldap index 0d98d169..eabc8983 160000 --- a/flext-dbt-ldap +++ b/flext-dbt-ldap @@ -1 +1 @@ -Subproject commit 0d98d169dd2d6dcd7b140a50474a160c69437968 +Subproject commit eabc8983a8f0f722c67f7f36bf99c51d3d66c96f diff --git a/flext-dbt-ldif b/flext-dbt-ldif index 9c12cbc0..b51ac1a5 160000 --- a/flext-dbt-ldif +++ b/flext-dbt-ldif @@ -1 +1 @@ -Subproject commit 9c12cbc02cdeb92ffec0c39373b16ec10cb82692 +Subproject commit b51ac1a5b40232d6cddfc755f9e45d05231c64b7 diff --git a/flext-dbt-oracle b/flext-dbt-oracle index 21e5f763..d9b350d5 160000 --- a/flext-dbt-oracle +++ b/flext-dbt-oracle @@ -1 +1 @@ -Subproject commit 21e5f7637d4e05c1dbb8d296ceef19c516940cf4 +Subproject commit d9b350d5536b0fa3e2ca798e211dd06e9cce5f76 diff --git a/flext-dbt-oracle-wms b/flext-dbt-oracle-wms index c5611b0e..c3d6c373 160000 --- a/flext-dbt-oracle-wms +++ b/flext-dbt-oracle-wms @@ -1 +1 @@ -Subproject commit c5611b0e29bb14e45629f59b8fb61db3c832dc55 +Subproject commit c3d6c373370d6162ce787c9254f7295f293975b9 diff --git a/flext-grpc b/flext-grpc index 27028cbb..f05b615f 160000 --- a/flext-grpc +++ b/flext-grpc @@ -1 +1 @@ -Subproject commit 27028cbb9cd70d95736406db0e36bbe331ab5d23 +Subproject commit f05b615fcd6fef41a210b1a830406379901af745 diff --git a/flext-ldap b/flext-ldap index dc43846f..5c5f5b16 160000 --- a/flext-ldap +++ b/flext-ldap @@ -1 +1 @@ -Subproject commit dc43846fa314c01f526fd1c1963cbbcdac60d806 +Subproject commit 5c5f5b165c95a60f3258c8c27e70644839962457 diff --git a/flext-ldif b/flext-ldif index d2ff3e25..7a24d778 160000 --- a/flext-ldif +++ b/flext-ldif @@ -1 +1 @@ -Subproject commit d2ff3e2589ec230aff8516c430466f0518ba34d4 +Subproject commit 7a24d778cb2ef985044b54fb714a6c512b1c1e9e diff --git a/flext-meltano b/flext-meltano index 6b68ff7b..135fc3b0 160000 --- a/flext-meltano +++ b/flext-meltano @@ -1 +1 @@ -Subproject commit 6b68ff7b0b9277e3e6c00e9b345b172874c50821 +Subproject commit 135fc3b088219d04f88906317a0b287611214233 diff --git a/flext-observability b/flext-observability index 71a7f9a0..ed55737a 160000 --- a/flext-observability +++ b/flext-observability @@ -1 +1 @@ -Subproject commit 71a7f9a0b2900362c2f5e79dfc96e31a249cb28d +Subproject commit ed55737aabb9e485701a404daee91d9263238d72 diff --git a/flext-oracle-oic b/flext-oracle-oic index 9c6db8e7..6a56fa75 160000 --- a/flext-oracle-oic +++ b/flext-oracle-oic @@ -1 +1 @@ -Subproject commit 9c6db8e733a7406bb86e684b9573fd8078268090 +Subproject commit 6a56fa75850d9ae629d1ed272c6094f243a8e382 From 665f6f015a6b5a87af342b29d09ddb7aab5f083e Mon Sep 17 00:00:00 2001 From: Marlon Costa Date: Fri, 20 Feb 2026 15:06:59 -0300 Subject: [PATCH 17/34] chore(workspace): checkpoint latest subproject updates --- flext-oracle-wms | 2 +- flext-plugin | 2 +- flext-quality | 2 +- flext-tap-ldap | 2 +- flext-tap-ldif | 2 +- flext-tap-oracle | 2 +- flext-tap-oracle-oic | 2 +- flext-tap-oracle-wms | 2 +- flext-target-ldap | 2 +- flext-target-ldif | 2 +- flext-target-oracle | 2 +- flext-target-oracle-oic | 2 +- flext-target-oracle-wms | 2 +- flext-web | 2 +- 14 files changed, 14 insertions(+), 14 deletions(-) diff --git a/flext-oracle-wms b/flext-oracle-wms index a3245e1c..74ce77ec 160000 --- a/flext-oracle-wms +++ b/flext-oracle-wms @@ -1 +1 @@ -Subproject commit a3245e1ce85fe19262e1ace453ced8724bc00acb +Subproject commit 74ce77ec40137a59571a53d3ed1db17dd64b5245 diff --git a/flext-plugin b/flext-plugin index 470603c4..d6502d0b 160000 --- a/flext-plugin +++ b/flext-plugin @@ -1 +1 @@ -Subproject commit 470603c44ec6d874d884e096c61fbd5b419c7963 +Subproject commit d6502d0be1795cfe942538b00871506faff0c892 diff --git a/flext-quality b/flext-quality index 9c6f3768..3b9e2a4f 160000 --- a/flext-quality +++ b/flext-quality @@ -1 +1 @@ -Subproject commit 9c6f376853daa4caca31aed04af11a355f3d45d6 +Subproject commit 3b9e2a4f59bfa36bba253024fbc8a23c65ba69a2 diff --git a/flext-tap-ldap b/flext-tap-ldap index 9b2b125f..6e9c433e 160000 --- a/flext-tap-ldap +++ b/flext-tap-ldap @@ -1 +1 @@ -Subproject commit 9b2b125febb67eb4d40047b6b3c644f3992bb163 +Subproject commit 6e9c433e11023503b7430d7e3e3caa64ce8e4642 diff --git a/flext-tap-ldif b/flext-tap-ldif index b401b8f2..0c7408d1 160000 --- a/flext-tap-ldif +++ b/flext-tap-ldif @@ -1 +1 @@ -Subproject commit b401b8f2788a0eb0c98af8b4fe492a494d3c2c15 +Subproject commit 0c7408d1cf79d0540d3e951c87b31f1200bee5de diff --git a/flext-tap-oracle b/flext-tap-oracle index be84fec4..35fd37a7 160000 --- a/flext-tap-oracle +++ b/flext-tap-oracle @@ -1 +1 @@ -Subproject commit be84fec47b3bfad3f7bf7358883a41693b4d5698 +Subproject commit 35fd37a75a8afe39eb7e36c9a771167eda9470fe diff --git a/flext-tap-oracle-oic b/flext-tap-oracle-oic index 066d985e..d4708085 160000 --- a/flext-tap-oracle-oic +++ b/flext-tap-oracle-oic @@ -1 +1 @@ -Subproject commit 066d985ea590ed01c4f3ee057c3b15a53fd4e7bd +Subproject commit d4708085ecd3f77ade06cf9cf58fe5ccf44efb70 diff --git a/flext-tap-oracle-wms b/flext-tap-oracle-wms index fe870ac4..334be909 160000 --- a/flext-tap-oracle-wms +++ b/flext-tap-oracle-wms @@ -1 +1 @@ -Subproject commit fe870ac42ae23c741713262a7f553a941cb37649 +Subproject commit 334be9090543cd129ca763210b8046a721801509 diff --git a/flext-target-ldap b/flext-target-ldap index 8d574440..8735bdcf 160000 --- a/flext-target-ldap +++ b/flext-target-ldap @@ -1 +1 @@ -Subproject commit 8d57444035ed0976c45da771341326e472f2ac42 +Subproject commit 8735bdcfac10c0b3ef872579e4984ec98cc8ab96 diff --git a/flext-target-ldif b/flext-target-ldif index 8469557a..33cf0dd7 160000 --- a/flext-target-ldif +++ b/flext-target-ldif @@ -1 +1 @@ -Subproject commit 8469557a02c5051c94c0aa94e0e95abd0186e835 +Subproject commit 33cf0dd7644f7c4d194c527107e9c0dad2bceaf7 diff --git a/flext-target-oracle b/flext-target-oracle index b296682a..c26107b1 160000 --- a/flext-target-oracle +++ b/flext-target-oracle @@ -1 +1 @@ -Subproject commit b296682a8b7f99d30aa6728d277924e41905a758 +Subproject commit c26107b1e3ee759f6e522bfc24116f724e2afa99 diff --git a/flext-target-oracle-oic b/flext-target-oracle-oic index bf02686e..3687d5a3 160000 --- a/flext-target-oracle-oic +++ b/flext-target-oracle-oic @@ -1 +1 @@ -Subproject commit bf02686e85a03071a59964d406820560f3b4c0f3 +Subproject commit 3687d5a34d5cdfc4aeec0bbb2709bb909890aa03 diff --git a/flext-target-oracle-wms b/flext-target-oracle-wms index aeb095d2..b7516453 160000 --- a/flext-target-oracle-wms +++ b/flext-target-oracle-wms @@ -1 +1 @@ -Subproject commit aeb095d26d09a5fd662f291d9e6b73ccd58f122a +Subproject commit b751645313908dde715e47905e83764979a65701 diff --git a/flext-web b/flext-web index ee84ee28..6f4db855 160000 --- a/flext-web +++ b/flext-web @@ -1 +1 @@ -Subproject commit ee84ee28ea5b1066f58193bf75ecd44efe7659c9 +Subproject commit 6f4db8554b5ff4baa6514555a5fa27c8916b88d9 From 6b15c18ffc9f6049e89909a29ab02fe32bfb8ef6 Mon Sep 17 00:00:00 2001 From: Marlon Costa Date: Fri, 20 Feb 2026 15:21:07 -0300 Subject: [PATCH 18/34] chore: checkpoint pending 0.11.0-dev changes --- flext-ldif | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flext-ldif b/flext-ldif index 7a24d778..373baff7 160000 --- a/flext-ldif +++ b/flext-ldif @@ -1 +1 @@ -Subproject commit 7a24d778cb2ef985044b54fb714a6c512b1c1e9e +Subproject commit 373baff7e4b75abdd11386077b841805753cc7a6 From 7c5b4de14ac3771ac635aa9b34735007ceb9a44d Mon Sep 17 00:00:00 2001 From: Marlon Costa Date: Fri, 20 Feb 2026 15:34:07 -0300 Subject: [PATCH 19/34] chore: checkpoint pending 0.11.0-dev changes --- scripts/github/pr_manager.py | 26 ++++++++- tests/unit/scripts/github/pr_manager_tests.py | 55 +++++++++++++++++-- 2 files changed, 75 insertions(+), 6 deletions(-) diff --git a/scripts/github/pr_manager.py b/scripts/github/pr_manager.py index 0c3c53ad..2e1a2f77 100644 --- a/scripts/github/pr_manager.py +++ b/scripts/github/pr_manager.py @@ -29,6 +29,23 @@ def _run_stream(command: list[str], cwd: Path) -> int: return result.returncode +def _run_stream_with_output(command: list[str], cwd: Path) -> tuple[int, str]: + result = subprocess.run( + command, + cwd=cwd, + capture_output=True, + text=True, + check=False, + ) + output_parts = [ + part.strip() for part in (result.stdout, result.stderr) if part.strip() + ] + output = "\n".join(output_parts) + if output: + print(output) + return result.returncode, output + + def _current_branch(repo_root: Path) -> str: return _run_capture(["git", "rev-parse", "--abbrev-ref", "HEAD"], repo_root) @@ -171,7 +188,14 @@ def _merge_pr( command.append("--auto") if delete_branch == 1: command.append("--delete-branch") - exit_code = _run_stream(command, repo_root) + exit_code, output = _run_stream_with_output(command, repo_root) + if exit_code != 0 and "not mergeable" in output: + update_code, _ = _run_stream_with_output( + ["gh", "pr", "update-branch", selector, "--rebase"], + repo_root, + ) + if update_code == 0: + exit_code, _ = _run_stream_with_output(command, repo_root) if exit_code == 0: print("status=merged") if release_on_merge == 1: diff --git a/tests/unit/scripts/github/pr_manager_tests.py b/tests/unit/scripts/github/pr_manager_tests.py index b67c3a32..49a5af04 100644 --- a/tests/unit/scripts/github/pr_manager_tests.py +++ b/tests/unit/scripts/github/pr_manager_tests.py @@ -168,14 +168,19 @@ def test_merge_triggers_release_dispatch_when_workspace_repo( _ = workflows.mkdir(parents=True) _ = (workflows / "release.yml").write_text("name: release\n", encoding="utf-8") - calls: list[list[str]] = [] + run_calls: list[list[str]] = [] + + def _fake_run_stream_with_output(command: list[str], _cwd: Path) -> tuple[int, str]: + run_calls.append(command) + return 0, "" def _fake_run_stream(command: list[str], _cwd: Path) -> int: - calls.append(command) + run_calls.append(command) if command[:3] == ["gh", "release", "view"]: return 1 return 0 + monkeypatch.setattr(mod, "_run_stream_with_output", _fake_run_stream_with_output) monkeypatch.setattr(mod, "_run_stream", _fake_run_stream) exit_code = mod._merge_pr( @@ -189,7 +194,47 @@ def _fake_run_stream(command: list[str], _cwd: Path) -> int: ) assert exit_code == 0 - assert calls[0] == ["gh", "pr", "merge", "123", "--squash", "--auto"] - assert calls[1] == ["gh", "release", "view", "v0.11.0"] - assert calls[2] == ["gh", "workflow", "run", "release.yml", "-f", "tag=v0.11.0"] + assert run_calls[0] == ["gh", "pr", "merge", "123", "--squash", "--auto"] + assert run_calls[1] == ["gh", "release", "view", "v0.11.0"] + assert run_calls[2] == ["gh", "workflow", "run", "release.yml", "-f", "tag=v0.11.0"] assert "status=release-dispatched tag=v0.11.0" in capsys.readouterr().out + + +def test_merge_retries_after_update_branch_on_non_mergeable( + monkeypatch: pytest.MonkeyPatch, tmp_path: Path +) -> None: + mod = _load_module("pr_manager_merge_retry", "scripts/github/pr_manager.py") + + calls: list[list[str]] = [] + responses = [ + (1, "X Pull request #7 is not mergeable"), + (0, "updated"), + (0, "merged"), + ] + + def _fake_run_stream_with_output(command: list[str], _cwd: Path) -> tuple[int, str]: + calls.append(command) + return responses.pop(0) + + def _fake_trigger_release_if_needed(repo_root: Path, head: str) -> None: + _ = repo_root, head + + monkeypatch.setattr(mod, "_run_stream_with_output", _fake_run_stream_with_output) + monkeypatch.setattr( + mod, "_trigger_release_if_needed", _fake_trigger_release_if_needed + ) + + exit_code = mod._merge_pr( + repo_root=tmp_path, + selector="7", + head="0.11.0-dev", + method="squash", + auto=0, + delete_branch=0, + release_on_merge=0, + ) + + assert exit_code == 0 + assert calls[0] == ["gh", "pr", "merge", "7", "--squash"] + assert calls[1] == ["gh", "pr", "update-branch", "7", "--rebase"] + assert calls[2] == ["gh", "pr", "merge", "7", "--squash"] From a3e211aff24708884280035718a1d740d0cc2d38 Mon Sep 17 00:00:00 2001 From: Marlon Costa Date: Fri, 20 Feb 2026 15:37:19 -0300 Subject: [PATCH 20/34] chore: checkpoint pending 0.11.0-dev changes --- flexcore | 2 +- flext-api | 2 +- flext-auth | 2 +- flext-cli | 2 +- flext-core | 2 +- flext-db-oracle | 2 +- flext-dbt-ldap | 2 +- flext-dbt-ldif | 2 +- flext-dbt-oracle | 2 +- flext-dbt-oracle-wms | 2 +- flext-grpc | 2 +- flext-ldap | 2 +- flext-ldif | 2 +- flext-meltano | 2 +- flext-observability | 2 +- flext-oracle-oic | 2 +- flext-oracle-wms | 2 +- flext-plugin | 2 +- flext-quality | 2 +- flext-tap-ldap | 2 +- flext-tap-ldif | 2 +- flext-tap-oracle | 2 +- flext-tap-oracle-oic | 2 +- flext-tap-oracle-wms | 2 +- flext-target-ldap | 2 +- flext-target-ldif | 2 +- flext-target-oracle | 2 +- flext-target-oracle-oic | 2 +- flext-target-oracle-wms | 2 +- flext-web | 2 +- 30 files changed, 30 insertions(+), 30 deletions(-) diff --git a/flexcore b/flexcore index e92413e4..a59ac2d5 160000 --- a/flexcore +++ b/flexcore @@ -1 +1 @@ -Subproject commit e92413e435e9f8977bca145ec4e68cf21e6c87e6 +Subproject commit a59ac2d5f48c2d13fb78d185c6533859463d8747 diff --git a/flext-api b/flext-api index 7ff470c2..2026d1ff 160000 --- a/flext-api +++ b/flext-api @@ -1 +1 @@ -Subproject commit 7ff470c220d5ba4a8a7a67e10643060aa80ea21b +Subproject commit 2026d1ff29ead5b2bb4430520fdacfa1917a3396 diff --git a/flext-auth b/flext-auth index 028f61c1..76d4eaaf 160000 --- a/flext-auth +++ b/flext-auth @@ -1 +1 @@ -Subproject commit 028f61c13ce4055cedfa31de82c1388f7d68124b +Subproject commit 76d4eaaf06306dd63e4407eebe1b1f5727b940f5 diff --git a/flext-cli b/flext-cli index 679dd76b..429b2079 160000 --- a/flext-cli +++ b/flext-cli @@ -1 +1 @@ -Subproject commit 679dd76b668e37d94ee5c5091f2a067632a2c7b5 +Subproject commit 429b207903ca43743cbf874739705f57f43c044d diff --git a/flext-core b/flext-core index 4c68d58a..d4e0b150 160000 --- a/flext-core +++ b/flext-core @@ -1 +1 @@ -Subproject commit 4c68d58aa5107677f7eefbdb828be867258805eb +Subproject commit d4e0b1502f46c7837387374b1cdad5854aa87a28 diff --git a/flext-db-oracle b/flext-db-oracle index 2ba7f106..9effe159 160000 --- a/flext-db-oracle +++ b/flext-db-oracle @@ -1 +1 @@ -Subproject commit 2ba7f106fbea482606e8a2252d9d73dcd2695cf3 +Subproject commit 9effe15965ce2da725a28ff18bfdc0894ba52f2f diff --git a/flext-dbt-ldap b/flext-dbt-ldap index eabc8983..95369a4a 160000 --- a/flext-dbt-ldap +++ b/flext-dbt-ldap @@ -1 +1 @@ -Subproject commit eabc8983a8f0f722c67f7f36bf99c51d3d66c96f +Subproject commit 95369a4a250597a64a556110bbeba91dc518d312 diff --git a/flext-dbt-ldif b/flext-dbt-ldif index b51ac1a5..19981824 160000 --- a/flext-dbt-ldif +++ b/flext-dbt-ldif @@ -1 +1 @@ -Subproject commit b51ac1a5b40232d6cddfc755f9e45d05231c64b7 +Subproject commit 19981824b04c8c76f6a9491a147ea0119ed83b2a diff --git a/flext-dbt-oracle b/flext-dbt-oracle index d9b350d5..2787613a 160000 --- a/flext-dbt-oracle +++ b/flext-dbt-oracle @@ -1 +1 @@ -Subproject commit d9b350d5536b0fa3e2ca798e211dd06e9cce5f76 +Subproject commit 2787613ae71b5459447b160c172e3379f4877f77 diff --git a/flext-dbt-oracle-wms b/flext-dbt-oracle-wms index c3d6c373..760aa6b9 160000 --- a/flext-dbt-oracle-wms +++ b/flext-dbt-oracle-wms @@ -1 +1 @@ -Subproject commit c3d6c373370d6162ce787c9254f7295f293975b9 +Subproject commit 760aa6b9bcd4e6a0fcbc1b36c0b6110b210eff47 diff --git a/flext-grpc b/flext-grpc index f05b615f..c00e255a 160000 --- a/flext-grpc +++ b/flext-grpc @@ -1 +1 @@ -Subproject commit f05b615fcd6fef41a210b1a830406379901af745 +Subproject commit c00e255acd5344e4c0cc5e9bc32f701a08191748 diff --git a/flext-ldap b/flext-ldap index 5c5f5b16..7875eb9e 160000 --- a/flext-ldap +++ b/flext-ldap @@ -1 +1 @@ -Subproject commit 5c5f5b165c95a60f3258c8c27e70644839962457 +Subproject commit 7875eb9e1faec0f2dbd478d5910376e8b4633038 diff --git a/flext-ldif b/flext-ldif index 373baff7..95056d1d 160000 --- a/flext-ldif +++ b/flext-ldif @@ -1 +1 @@ -Subproject commit 373baff7e4b75abdd11386077b841805753cc7a6 +Subproject commit 95056d1d514e5ac7e97c6cb54e3959bf34f30ea7 diff --git a/flext-meltano b/flext-meltano index 135fc3b0..17456d2c 160000 --- a/flext-meltano +++ b/flext-meltano @@ -1 +1 @@ -Subproject commit 135fc3b088219d04f88906317a0b287611214233 +Subproject commit 17456d2c5c2b0c0f0a5829e8b9322ac51b0b9e16 diff --git a/flext-observability b/flext-observability index ed55737a..ee2bd666 160000 --- a/flext-observability +++ b/flext-observability @@ -1 +1 @@ -Subproject commit ed55737aabb9e485701a404daee91d9263238d72 +Subproject commit ee2bd666f13c61df6f4a1bf7e9b04db10b19249f diff --git a/flext-oracle-oic b/flext-oracle-oic index 6a56fa75..3d1e9084 160000 --- a/flext-oracle-oic +++ b/flext-oracle-oic @@ -1 +1 @@ -Subproject commit 6a56fa75850d9ae629d1ed272c6094f243a8e382 +Subproject commit 3d1e9084b5c802267efec52097a306950c4faf4b diff --git a/flext-oracle-wms b/flext-oracle-wms index 74ce77ec..24a64b49 160000 --- a/flext-oracle-wms +++ b/flext-oracle-wms @@ -1 +1 @@ -Subproject commit 74ce77ec40137a59571a53d3ed1db17dd64b5245 +Subproject commit 24a64b49bbcc6c72cf69636ca09b84fabf769d08 diff --git a/flext-plugin b/flext-plugin index d6502d0b..c30da664 160000 --- a/flext-plugin +++ b/flext-plugin @@ -1 +1 @@ -Subproject commit d6502d0be1795cfe942538b00871506faff0c892 +Subproject commit c30da6645338979d7bb5a4b482698d3192993acf diff --git a/flext-quality b/flext-quality index 3b9e2a4f..cdda0a43 160000 --- a/flext-quality +++ b/flext-quality @@ -1 +1 @@ -Subproject commit 3b9e2a4f59bfa36bba253024fbc8a23c65ba69a2 +Subproject commit cdda0a43b380037d95745a99491365176230511c diff --git a/flext-tap-ldap b/flext-tap-ldap index 6e9c433e..a85e90e6 160000 --- a/flext-tap-ldap +++ b/flext-tap-ldap @@ -1 +1 @@ -Subproject commit 6e9c433e11023503b7430d7e3e3caa64ce8e4642 +Subproject commit a85e90e6243a80d2848523d07eec43e998919723 diff --git a/flext-tap-ldif b/flext-tap-ldif index 0c7408d1..7e6763ac 160000 --- a/flext-tap-ldif +++ b/flext-tap-ldif @@ -1 +1 @@ -Subproject commit 0c7408d1cf79d0540d3e951c87b31f1200bee5de +Subproject commit 7e6763ace67fab3ef5fc381a851fc98fdced69c8 diff --git a/flext-tap-oracle b/flext-tap-oracle index 35fd37a7..2c75ed9b 160000 --- a/flext-tap-oracle +++ b/flext-tap-oracle @@ -1 +1 @@ -Subproject commit 35fd37a75a8afe39eb7e36c9a771167eda9470fe +Subproject commit 2c75ed9b8752ad2ddc7684b76b299388585c9992 diff --git a/flext-tap-oracle-oic b/flext-tap-oracle-oic index d4708085..7a296c6c 160000 --- a/flext-tap-oracle-oic +++ b/flext-tap-oracle-oic @@ -1 +1 @@ -Subproject commit d4708085ecd3f77ade06cf9cf58fe5ccf44efb70 +Subproject commit 7a296c6c1134a62294079a53b1c73da780652164 diff --git a/flext-tap-oracle-wms b/flext-tap-oracle-wms index 334be909..8da7b41f 160000 --- a/flext-tap-oracle-wms +++ b/flext-tap-oracle-wms @@ -1 +1 @@ -Subproject commit 334be9090543cd129ca763210b8046a721801509 +Subproject commit 8da7b41fb9bb31fd0cf193e698de9ccd522ff99b diff --git a/flext-target-ldap b/flext-target-ldap index 8735bdcf..80a7786e 160000 --- a/flext-target-ldap +++ b/flext-target-ldap @@ -1 +1 @@ -Subproject commit 8735bdcfac10c0b3ef872579e4984ec98cc8ab96 +Subproject commit 80a7786eb3673361355f22fded8d34ba6c90693e diff --git a/flext-target-ldif b/flext-target-ldif index 33cf0dd7..01f94b7e 160000 --- a/flext-target-ldif +++ b/flext-target-ldif @@ -1 +1 @@ -Subproject commit 33cf0dd7644f7c4d194c527107e9c0dad2bceaf7 +Subproject commit 01f94b7e1021633eb6098341340ba570180dd3a2 diff --git a/flext-target-oracle b/flext-target-oracle index c26107b1..8d1fd589 160000 --- a/flext-target-oracle +++ b/flext-target-oracle @@ -1 +1 @@ -Subproject commit c26107b1e3ee759f6e522bfc24116f724e2afa99 +Subproject commit 8d1fd5891343ed479c1cbc8d67d2c38d5ceb3f74 diff --git a/flext-target-oracle-oic b/flext-target-oracle-oic index 3687d5a3..aaf3a67f 160000 --- a/flext-target-oracle-oic +++ b/flext-target-oracle-oic @@ -1 +1 @@ -Subproject commit 3687d5a34d5cdfc4aeec0bbb2709bb909890aa03 +Subproject commit aaf3a67f5f1cd65a23a2dd8967d3f376a137a883 diff --git a/flext-target-oracle-wms b/flext-target-oracle-wms index b7516453..76bd2b6b 160000 --- a/flext-target-oracle-wms +++ b/flext-target-oracle-wms @@ -1 +1 @@ -Subproject commit b751645313908dde715e47905e83764979a65701 +Subproject commit 76bd2b6b49742735f6227f66a16f7e4ec0d0b406 diff --git a/flext-web b/flext-web index 6f4db855..0b393742 160000 --- a/flext-web +++ b/flext-web @@ -1 +1 @@ -Subproject commit 6f4db8554b5ff4baa6514555a5fa27c8916b88d9 +Subproject commit 0b393742176dd873bc9206f498d05495a0b5052d From 12489299c319efda695076fa81c31d65d4846d1b Mon Sep 17 00:00:00 2001 From: Marlon Costa Date: Fri, 20 Feb 2026 15:38:02 -0300 Subject: [PATCH 21/34] chore(workspace): checkpoint flext-ldif pending updates --- flext-ldif | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flext-ldif b/flext-ldif index 95056d1d..3a0a191c 160000 --- a/flext-ldif +++ b/flext-ldif @@ -1 +1 @@ -Subproject commit 95056d1d514e5ac7e97c6cb54e3959bf34f30ea7 +Subproject commit 3a0a191c0b9cc61f375f80dd23ddb94be44e19b6 From 668fcaee311ae045595a41b3eefe065aa6610c78 Mon Sep 17 00:00:00 2001 From: Marlon Costa Date: Fri, 20 Feb 2026 15:47:00 -0300 Subject: [PATCH 22/34] chore: checkpoint pending 0.11.0-dev changes --- flext-ldif | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flext-ldif b/flext-ldif index 3a0a191c..7ea79b50 160000 --- a/flext-ldif +++ b/flext-ldif @@ -1 +1 @@ -Subproject commit 3a0a191c0b9cc61f375f80dd23ddb94be44e19b6 +Subproject commit 7ea79b506af53787211f4096c9f5564460c3172e From d2114f5282e85546b5d0b7b8acfd17d226dc75a2 Mon Sep 17 00:00:00 2001 From: Marlon Costa Date: Fri, 20 Feb 2026 15:48:49 -0300 Subject: [PATCH 23/34] chore: checkpoint pending 0.11.0-dev changes --- flext-ldif | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flext-ldif b/flext-ldif index 7ea79b50..369070b8 160000 --- a/flext-ldif +++ b/flext-ldif @@ -1 +1 @@ -Subproject commit 7ea79b506af53787211f4096c9f5564460c3172e +Subproject commit 369070b887fb73d8340be876324f2e606d80d52b From 98c09048e43a10ced607d6d9f0bd565e225684aa Mon Sep 17 00:00:00 2001 From: Marlon Costa Date: Fri, 20 Feb 2026 15:52:37 -0300 Subject: [PATCH 24/34] chore(workspace): checkpoint flext-ldif updates --- flext-ldif | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flext-ldif b/flext-ldif index 369070b8..84d44fe1 160000 --- a/flext-ldif +++ b/flext-ldif @@ -1 +1 @@ -Subproject commit 369070b887fb73d8340be876324f2e606d80d52b +Subproject commit 84d44fe15578d48a73a61aa17f6bcffde0976493 From 9a66b10c425fdb15ddd3dd26a0c5648f1e582ded Mon Sep 17 00:00:00 2001 From: Marlon Costa Date: Fri, 20 Feb 2026 15:54:11 -0300 Subject: [PATCH 25/34] chore: checkpoint pending 0.11.0-dev changes --- flext-ldif | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flext-ldif b/flext-ldif index 84d44fe1..ad845c82 160000 --- a/flext-ldif +++ b/flext-ldif @@ -1 +1 @@ -Subproject commit 84d44fe15578d48a73a61aa17f6bcffde0976493 +Subproject commit ad845c8259256b379a5030bf11c352259c431a46 From db4e43173e88b3fc5adc3747dc180d97a5a2c14d Mon Sep 17 00:00:00 2001 From: Marlon Costa Date: Fri, 20 Feb 2026 15:56:10 -0300 Subject: [PATCH 26/34] chore: checkpoint pending 0.11.0-dev changes --- flexcore | 2 +- flext-api | 2 +- flext-auth | 2 +- flext-cli | 2 +- flext-core | 2 +- flext-db-oracle | 2 +- flext-dbt-ldap | 2 +- flext-dbt-ldif | 2 +- flext-dbt-oracle | 2 +- flext-dbt-oracle-wms | 2 +- flext-grpc | 2 +- flext-ldap | 2 +- flext-meltano | 2 +- flext-observability | 2 +- flext-oracle-oic | 2 +- 15 files changed, 15 insertions(+), 15 deletions(-) diff --git a/flexcore b/flexcore index a59ac2d5..0db6eae9 160000 --- a/flexcore +++ b/flexcore @@ -1 +1 @@ -Subproject commit a59ac2d5f48c2d13fb78d185c6533859463d8747 +Subproject commit 0db6eae94d8e3a64cce8fdd47dae409cd4947046 diff --git a/flext-api b/flext-api index 2026d1ff..10234edf 160000 --- a/flext-api +++ b/flext-api @@ -1 +1 @@ -Subproject commit 2026d1ff29ead5b2bb4430520fdacfa1917a3396 +Subproject commit 10234edf2d143fbc1fb7ae7cc094e9f694c5c13f diff --git a/flext-auth b/flext-auth index 76d4eaaf..f1f96410 160000 --- a/flext-auth +++ b/flext-auth @@ -1 +1 @@ -Subproject commit 76d4eaaf06306dd63e4407eebe1b1f5727b940f5 +Subproject commit f1f964105c38dfe5f976b3bc5182f2ee1661ea6b diff --git a/flext-cli b/flext-cli index 429b2079..1b34d821 160000 --- a/flext-cli +++ b/flext-cli @@ -1 +1 @@ -Subproject commit 429b207903ca43743cbf874739705f57f43c044d +Subproject commit 1b34d8217d5407fb5c904f1bfa5c7eb3c60718bb diff --git a/flext-core b/flext-core index d4e0b150..587cc629 160000 --- a/flext-core +++ b/flext-core @@ -1 +1 @@ -Subproject commit d4e0b1502f46c7837387374b1cdad5854aa87a28 +Subproject commit 587cc629a9627eccdf0777501bb60d106f0e174d diff --git a/flext-db-oracle b/flext-db-oracle index 9effe159..ee740766 160000 --- a/flext-db-oracle +++ b/flext-db-oracle @@ -1 +1 @@ -Subproject commit 9effe15965ce2da725a28ff18bfdc0894ba52f2f +Subproject commit ee74076628f93498ffa5d3a91cb3a7593a2fadbd diff --git a/flext-dbt-ldap b/flext-dbt-ldap index 95369a4a..ef36ab39 160000 --- a/flext-dbt-ldap +++ b/flext-dbt-ldap @@ -1 +1 @@ -Subproject commit 95369a4a250597a64a556110bbeba91dc518d312 +Subproject commit ef36ab39a14c92ab301a4ef7fc9192ec0ac9a880 diff --git a/flext-dbt-ldif b/flext-dbt-ldif index 19981824..dba60524 160000 --- a/flext-dbt-ldif +++ b/flext-dbt-ldif @@ -1 +1 @@ -Subproject commit 19981824b04c8c76f6a9491a147ea0119ed83b2a +Subproject commit dba605242f7cc3f2a27d9b1116ca6593da252e4e diff --git a/flext-dbt-oracle b/flext-dbt-oracle index 2787613a..22b823b5 160000 --- a/flext-dbt-oracle +++ b/flext-dbt-oracle @@ -1 +1 @@ -Subproject commit 2787613ae71b5459447b160c172e3379f4877f77 +Subproject commit 22b823b55b409ad966f9805ce75cd62a0c681f26 diff --git a/flext-dbt-oracle-wms b/flext-dbt-oracle-wms index 760aa6b9..20ce7238 160000 --- a/flext-dbt-oracle-wms +++ b/flext-dbt-oracle-wms @@ -1 +1 @@ -Subproject commit 760aa6b9bcd4e6a0fcbc1b36c0b6110b210eff47 +Subproject commit 20ce7238abbeb50db20e7420f47e3971d78ea915 diff --git a/flext-grpc b/flext-grpc index c00e255a..9266314f 160000 --- a/flext-grpc +++ b/flext-grpc @@ -1 +1 @@ -Subproject commit c00e255acd5344e4c0cc5e9bc32f701a08191748 +Subproject commit 9266314fa665dd4ca40553f3037ab16220a977b2 diff --git a/flext-ldap b/flext-ldap index 7875eb9e..2480b496 160000 --- a/flext-ldap +++ b/flext-ldap @@ -1 +1 @@ -Subproject commit 7875eb9e1faec0f2dbd478d5910376e8b4633038 +Subproject commit 2480b4962cea95820b1ffa2126fcfc8285d8845d diff --git a/flext-meltano b/flext-meltano index 17456d2c..12228ee3 160000 --- a/flext-meltano +++ b/flext-meltano @@ -1 +1 @@ -Subproject commit 17456d2c5c2b0c0f0a5829e8b9322ac51b0b9e16 +Subproject commit 12228ee3e94921d2d8ed51cb29d0c77bd427856f diff --git a/flext-observability b/flext-observability index ee2bd666..ca4597eb 160000 --- a/flext-observability +++ b/flext-observability @@ -1 +1 @@ -Subproject commit ee2bd666f13c61df6f4a1bf7e9b04db10b19249f +Subproject commit ca4597eb26db1f5afb80069d24480481a256034a diff --git a/flext-oracle-oic b/flext-oracle-oic index 3d1e9084..5e900863 160000 --- a/flext-oracle-oic +++ b/flext-oracle-oic @@ -1 +1 @@ -Subproject commit 3d1e9084b5c802267efec52097a306950c4faf4b +Subproject commit 5e900863ac19fb1b245817b06e02e891ea407109 From 3f838334bb3ee8360771c6c99a192b632d63ca1d Mon Sep 17 00:00:00 2001 From: Marlon Costa Date: Fri, 20 Feb 2026 16:00:23 -0300 Subject: [PATCH 27/34] chore(workspace): checkpoint rebased subproject updates --- flexcore | 2 +- flext-api | 2 +- flext-auth | 2 +- flext-cli | 2 +- flext-db-oracle | 2 +- flext-dbt-ldap | 2 +- flext-dbt-ldif | 2 +- flext-dbt-oracle | 2 +- flext-dbt-oracle-wms | 2 +- flext-grpc | 2 +- flext-ldap | 2 +- flext-ldif | 2 +- flext-meltano | 2 +- flext-observability | 2 +- flext-oracle-oic | 2 +- flext-oracle-wms | 2 +- flext-plugin | 2 +- flext-quality | 2 +- flext-tap-ldap | 2 +- flext-tap-ldif | 2 +- flext-tap-oracle | 2 +- flext-tap-oracle-oic | 2 +- flext-tap-oracle-wms | 2 +- flext-target-ldap | 2 +- flext-target-ldif | 2 +- flext-target-oracle | 2 +- flext-target-oracle-oic | 2 +- flext-target-oracle-wms | 2 +- flext-web | 2 +- 29 files changed, 29 insertions(+), 29 deletions(-) diff --git a/flexcore b/flexcore index 0db6eae9..0b17af00 160000 --- a/flexcore +++ b/flexcore @@ -1 +1 @@ -Subproject commit 0db6eae94d8e3a64cce8fdd47dae409cd4947046 +Subproject commit 0b17af00ea4d96b27f96c40c670bd21341aa2052 diff --git a/flext-api b/flext-api index 10234edf..5b73dda9 160000 --- a/flext-api +++ b/flext-api @@ -1 +1 @@ -Subproject commit 10234edf2d143fbc1fb7ae7cc094e9f694c5c13f +Subproject commit 5b73dda9ac578070f92066a0aab1b32d286eea49 diff --git a/flext-auth b/flext-auth index f1f96410..a0293feb 160000 --- a/flext-auth +++ b/flext-auth @@ -1 +1 @@ -Subproject commit f1f964105c38dfe5f976b3bc5182f2ee1661ea6b +Subproject commit a0293feba246775e2c7cdb7556a1179a108f1ae1 diff --git a/flext-cli b/flext-cli index 1b34d821..b8f36721 160000 --- a/flext-cli +++ b/flext-cli @@ -1 +1 @@ -Subproject commit 1b34d8217d5407fb5c904f1bfa5c7eb3c60718bb +Subproject commit b8f36721d2dacc148d02050281bff0f8f9a51019 diff --git a/flext-db-oracle b/flext-db-oracle index ee740766..4070a079 160000 --- a/flext-db-oracle +++ b/flext-db-oracle @@ -1 +1 @@ -Subproject commit ee74076628f93498ffa5d3a91cb3a7593a2fadbd +Subproject commit 4070a07905eed7fff27dcbb4ef45edcb8ba90691 diff --git a/flext-dbt-ldap b/flext-dbt-ldap index ef36ab39..dc55e8fc 160000 --- a/flext-dbt-ldap +++ b/flext-dbt-ldap @@ -1 +1 @@ -Subproject commit ef36ab39a14c92ab301a4ef7fc9192ec0ac9a880 +Subproject commit dc55e8fcec95449b25447d0e374a4f6a9db46e57 diff --git a/flext-dbt-ldif b/flext-dbt-ldif index dba60524..e7c83a1a 160000 --- a/flext-dbt-ldif +++ b/flext-dbt-ldif @@ -1 +1 @@ -Subproject commit dba605242f7cc3f2a27d9b1116ca6593da252e4e +Subproject commit e7c83a1accd3544eb7d8855e8c5411ebc98f68f8 diff --git a/flext-dbt-oracle b/flext-dbt-oracle index 22b823b5..5f4b0b7e 160000 --- a/flext-dbt-oracle +++ b/flext-dbt-oracle @@ -1 +1 @@ -Subproject commit 22b823b55b409ad966f9805ce75cd62a0c681f26 +Subproject commit 5f4b0b7e4ea89a1c462960a40f070122c4ba0510 diff --git a/flext-dbt-oracle-wms b/flext-dbt-oracle-wms index 20ce7238..2ca3ea48 160000 --- a/flext-dbt-oracle-wms +++ b/flext-dbt-oracle-wms @@ -1 +1 @@ -Subproject commit 20ce7238abbeb50db20e7420f47e3971d78ea915 +Subproject commit 2ca3ea4844f4bf569c56527c74b904b457905ac8 diff --git a/flext-grpc b/flext-grpc index 9266314f..e2225e29 160000 --- a/flext-grpc +++ b/flext-grpc @@ -1 +1 @@ -Subproject commit 9266314fa665dd4ca40553f3037ab16220a977b2 +Subproject commit e2225e29d20764d21b578c19993be0e4a7baed43 diff --git a/flext-ldap b/flext-ldap index 2480b496..50896ab6 160000 --- a/flext-ldap +++ b/flext-ldap @@ -1 +1 @@ -Subproject commit 2480b4962cea95820b1ffa2126fcfc8285d8845d +Subproject commit 50896ab6a002645d647bd0243971eebee69ec3cc diff --git a/flext-ldif b/flext-ldif index ad845c82..52253cb0 160000 --- a/flext-ldif +++ b/flext-ldif @@ -1 +1 @@ -Subproject commit ad845c8259256b379a5030bf11c352259c431a46 +Subproject commit 52253cb0e04a50a241891c6d9a31e855f8d44570 diff --git a/flext-meltano b/flext-meltano index 12228ee3..92052e97 160000 --- a/flext-meltano +++ b/flext-meltano @@ -1 +1 @@ -Subproject commit 12228ee3e94921d2d8ed51cb29d0c77bd427856f +Subproject commit 92052e97da161a92d6ecb81fbdfc79712e158624 diff --git a/flext-observability b/flext-observability index ca4597eb..f2a76201 160000 --- a/flext-observability +++ b/flext-observability @@ -1 +1 @@ -Subproject commit ca4597eb26db1f5afb80069d24480481a256034a +Subproject commit f2a76201cfc070e7c845a8729793fcf875a9d7c9 diff --git a/flext-oracle-oic b/flext-oracle-oic index 5e900863..21ca240a 160000 --- a/flext-oracle-oic +++ b/flext-oracle-oic @@ -1 +1 @@ -Subproject commit 5e900863ac19fb1b245817b06e02e891ea407109 +Subproject commit 21ca240a4f8cf71b6d24dcb195741b958ebc83f9 diff --git a/flext-oracle-wms b/flext-oracle-wms index 24a64b49..043ac441 160000 --- a/flext-oracle-wms +++ b/flext-oracle-wms @@ -1 +1 @@ -Subproject commit 24a64b49bbcc6c72cf69636ca09b84fabf769d08 +Subproject commit 043ac44132e28e979e498477a5cd4c3274071ef6 diff --git a/flext-plugin b/flext-plugin index c30da664..1c3bae35 160000 --- a/flext-plugin +++ b/flext-plugin @@ -1 +1 @@ -Subproject commit c30da6645338979d7bb5a4b482698d3192993acf +Subproject commit 1c3bae355e5f6a13a60c4ab1bea97283e71652f0 diff --git a/flext-quality b/flext-quality index cdda0a43..2a0324aa 160000 --- a/flext-quality +++ b/flext-quality @@ -1 +1 @@ -Subproject commit cdda0a43b380037d95745a99491365176230511c +Subproject commit 2a0324aab3a946b1fc24e82d12d4cdbf393679b2 diff --git a/flext-tap-ldap b/flext-tap-ldap index a85e90e6..9d7a1a08 160000 --- a/flext-tap-ldap +++ b/flext-tap-ldap @@ -1 +1 @@ -Subproject commit a85e90e6243a80d2848523d07eec43e998919723 +Subproject commit 9d7a1a081bba7ac0bd7a83c7e64cca13a3079185 diff --git a/flext-tap-ldif b/flext-tap-ldif index 7e6763ac..1780e11e 160000 --- a/flext-tap-ldif +++ b/flext-tap-ldif @@ -1 +1 @@ -Subproject commit 7e6763ace67fab3ef5fc381a851fc98fdced69c8 +Subproject commit 1780e11ef0de586f075c5abf81b87c7cc01ac04b diff --git a/flext-tap-oracle b/flext-tap-oracle index 2c75ed9b..e56bbf3e 160000 --- a/flext-tap-oracle +++ b/flext-tap-oracle @@ -1 +1 @@ -Subproject commit 2c75ed9b8752ad2ddc7684b76b299388585c9992 +Subproject commit e56bbf3e9b13d7250d008e5b505a0b9c68e8da2b diff --git a/flext-tap-oracle-oic b/flext-tap-oracle-oic index 7a296c6c..2d9e2b25 160000 --- a/flext-tap-oracle-oic +++ b/flext-tap-oracle-oic @@ -1 +1 @@ -Subproject commit 7a296c6c1134a62294079a53b1c73da780652164 +Subproject commit 2d9e2b250aa0736ff0f9b81ebdb241c876753002 diff --git a/flext-tap-oracle-wms b/flext-tap-oracle-wms index 8da7b41f..d07b3e1a 160000 --- a/flext-tap-oracle-wms +++ b/flext-tap-oracle-wms @@ -1 +1 @@ -Subproject commit 8da7b41fb9bb31fd0cf193e698de9ccd522ff99b +Subproject commit d07b3e1a5b3763a4c913d6b5ab67b6f244797525 diff --git a/flext-target-ldap b/flext-target-ldap index 80a7786e..82637e20 160000 --- a/flext-target-ldap +++ b/flext-target-ldap @@ -1 +1 @@ -Subproject commit 80a7786eb3673361355f22fded8d34ba6c90693e +Subproject commit 82637e205c2d2ae82b51b5effbeac6ef9944d3df diff --git a/flext-target-ldif b/flext-target-ldif index 01f94b7e..d91feb0c 160000 --- a/flext-target-ldif +++ b/flext-target-ldif @@ -1 +1 @@ -Subproject commit 01f94b7e1021633eb6098341340ba570180dd3a2 +Subproject commit d91feb0ca079166896279bce6b512aac4d589453 diff --git a/flext-target-oracle b/flext-target-oracle index 8d1fd589..7e65c69c 160000 --- a/flext-target-oracle +++ b/flext-target-oracle @@ -1 +1 @@ -Subproject commit 8d1fd5891343ed479c1cbc8d67d2c38d5ceb3f74 +Subproject commit 7e65c69c801f3ffb9ce12b9216b6b84dd8373caf diff --git a/flext-target-oracle-oic b/flext-target-oracle-oic index aaf3a67f..22affd73 160000 --- a/flext-target-oracle-oic +++ b/flext-target-oracle-oic @@ -1 +1 @@ -Subproject commit aaf3a67f5f1cd65a23a2dd8967d3f376a137a883 +Subproject commit 22affd73c653e4e489c91f20e03b90c81263d3d7 diff --git a/flext-target-oracle-wms b/flext-target-oracle-wms index 76bd2b6b..9c9ac660 160000 --- a/flext-target-oracle-wms +++ b/flext-target-oracle-wms @@ -1 +1 @@ -Subproject commit 76bd2b6b49742735f6227f66a16f7e4ec0d0b406 +Subproject commit 9c9ac6604e45e94ddc894008f2fb43bfd7940af1 diff --git a/flext-web b/flext-web index 0b393742..a41219bc 160000 --- a/flext-web +++ b/flext-web @@ -1 +1 @@ -Subproject commit 0b393742176dd873bc9206f498d05495a0b5052d +Subproject commit a41219bc03b7c42a6c53e1f8a191f1443a966328 From 32ff8d59638ec4e1e90d89ef2f9c81508879040b Mon Sep 17 00:00:00 2001 From: Marlon Costa Date: Fri, 20 Feb 2026 16:01:14 -0300 Subject: [PATCH 28/34] chore: checkpoint pending 0.11.0-dev changes --- flext-ldif | 2 +- flext-oracle-wms | 2 +- flext-plugin | 2 +- flext-quality | 2 +- flext-tap-ldap | 2 +- flext-tap-ldif | 2 +- scripts/github/pr_workspace.py | 78 +++++++++++++++------- tests/scripts/github/test_pr_workspace.py | 80 +++++++++++++++++++++++ 8 files changed, 142 insertions(+), 28 deletions(-) diff --git a/flext-ldif b/flext-ldif index 52253cb0..7a014cc2 160000 --- a/flext-ldif +++ b/flext-ldif @@ -1 +1 @@ -Subproject commit 52253cb0e04a50a241891c6d9a31e855f8d44570 +Subproject commit 7a014cc27c1ab442adfeab8594b8f11bb0fa66c4 diff --git a/flext-oracle-wms b/flext-oracle-wms index 043ac441..1313f288 160000 --- a/flext-oracle-wms +++ b/flext-oracle-wms @@ -1 +1 @@ -Subproject commit 043ac44132e28e979e498477a5cd4c3274071ef6 +Subproject commit 1313f2884396f937de580815c66167b07e9a3a3b diff --git a/flext-plugin b/flext-plugin index 1c3bae35..4e344862 160000 --- a/flext-plugin +++ b/flext-plugin @@ -1 +1 @@ -Subproject commit 1c3bae355e5f6a13a60c4ab1bea97283e71652f0 +Subproject commit 4e3448629adabc802a7036adda82ccedbcc0e711 diff --git a/flext-quality b/flext-quality index 2a0324aa..fad455b5 160000 --- a/flext-quality +++ b/flext-quality @@ -1 +1 @@ -Subproject commit 2a0324aab3a946b1fc24e82d12d4cdbf393679b2 +Subproject commit fad455b51ec0607cdc524ed0ef72daddf30f2542 diff --git a/flext-tap-ldap b/flext-tap-ldap index 9d7a1a08..f3528f07 160000 --- a/flext-tap-ldap +++ b/flext-tap-ldap @@ -1 +1 @@ -Subproject commit 9d7a1a081bba7ac0bd7a83c7e64cca13a3079185 +Subproject commit f3528f074643aab5264c0230a4a40fa21a8c9fe0 diff --git a/flext-tap-ldif b/flext-tap-ldif index 1780e11e..25debf8b 160000 --- a/flext-tap-ldif +++ b/flext-tap-ldif @@ -1 +1 @@ -Subproject commit 1780e11ef0de586f075c5abf81b87c7cc01ac04b +Subproject commit 25debf8b6740813b818306e99e7d8f584695c0e9 diff --git a/scripts/github/pr_workspace.py b/scripts/github/pr_workspace.py index 1e000107..c2962743 100644 --- a/scripts/github/pr_workspace.py +++ b/scripts/github/pr_workspace.py @@ -114,28 +114,60 @@ def _run_pr(repo_root: Path, workspace_root: Path, args: argparse.Namespace) -> report_dir.mkdir(parents=True, exist_ok=True) display = _repo_display_name(repo_root, workspace_root) log_path = report_dir / f"{display}.log" - command = [ - "make", - "-C", - str(repo_root), - "pr", - f"PR_ACTION={args.pr_action}", - f"PR_BASE={args.pr_base}", - f"PR_DRAFT={args.pr_draft}", - f"PR_MERGE_METHOD={args.pr_merge_method}", - f"PR_AUTO={args.pr_auto}", - f"PR_DELETE_BRANCH={args.pr_delete_branch}", - f"PR_CHECKS_STRICT={args.pr_checks_strict}", - f"PR_RELEASE_ON_MERGE={args.pr_release_on_merge}", - ] - if args.pr_head: - command.append(f"PR_HEAD={args.pr_head}") - if args.pr_number: - command.append(f"PR_NUMBER={args.pr_number}") - if args.pr_title: - command.append(f"PR_TITLE={args.pr_title}") - if args.pr_body: - command.append(f"PR_BODY={args.pr_body}") + if repo_root == workspace_root: + command = [ + "python", + "scripts/github/pr_manager.py", + "--repo-root", + str(repo_root), + "--action", + args.pr_action, + "--base", + args.pr_base, + "--draft", + args.pr_draft, + "--merge-method", + args.pr_merge_method, + "--auto", + args.pr_auto, + "--delete-branch", + args.pr_delete_branch, + "--checks-strict", + args.pr_checks_strict, + "--release-on-merge", + args.pr_release_on_merge, + ] + if args.pr_head: + command.extend(["--head", args.pr_head]) + if args.pr_number: + command.extend(["--number", args.pr_number]) + if args.pr_title: + command.extend(["--title", args.pr_title]) + if args.pr_body: + command.extend(["--body", args.pr_body]) + else: + command = [ + "make", + "-C", + str(repo_root), + "pr", + f"PR_ACTION={args.pr_action}", + f"PR_BASE={args.pr_base}", + f"PR_DRAFT={args.pr_draft}", + f"PR_MERGE_METHOD={args.pr_merge_method}", + f"PR_AUTO={args.pr_auto}", + f"PR_DELETE_BRANCH={args.pr_delete_branch}", + f"PR_CHECKS_STRICT={args.pr_checks_strict}", + f"PR_RELEASE_ON_MERGE={args.pr_release_on_merge}", + ] + if args.pr_head: + command.append(f"PR_HEAD={args.pr_head}") + if args.pr_number: + command.append(f"PR_NUMBER={args.pr_number}") + if args.pr_title: + command.append(f"PR_TITLE={args.pr_title}") + if args.pr_body: + command.append(f"PR_BODY={args.pr_body}") started = time.monotonic() with log_path.open("w", encoding="utf-8") as handle: @@ -160,6 +192,8 @@ def main() -> int: failures = 0 for repo_root in repos: + display = _repo_display_name(repo_root, workspace_root) + print(f"[RUN] {display}", flush=True) _checkout_branch(repo_root, args.branch) if args.checkpoint == 1: _checkpoint(repo_root, args.branch) diff --git a/tests/scripts/github/test_pr_workspace.py b/tests/scripts/github/test_pr_workspace.py index 7fdca469..ab662bfb 100644 --- a/tests/scripts/github/test_pr_workspace.py +++ b/tests/scripts/github/test_pr_workspace.py @@ -148,3 +148,83 @@ def _run_pr(repo: Path, _root: Path, _args: Any) -> int: assert mod.main() == 1 assert seen == [proj_a] + + +def test_run_pr_uses_pr_manager_for_workspace_root( + monkeypatch: pytest.MonkeyPatch, + tmp_path: Path, +) -> None: + mod = _load_module("pr_workspace_root_command", "scripts/github/pr_workspace.py") + workspace = tmp_path / "workspace" + _ = workspace.mkdir(parents=True) + + commands: list[list[str]] = [] + + def _fake_run(command: list[str], **_kwargs: Any) -> Any: + commands.append(command) + return SimpleNamespace(returncode=0) + + monkeypatch.setattr(mod.subprocess, "run", _fake_run) + + args = mod.argparse.Namespace( + pr_action="status", + pr_base="main", + pr_head="", + pr_number="", + pr_title="", + pr_body="", + pr_draft="0", + pr_merge_method="squash", + pr_auto="0", + pr_delete_branch="0", + pr_checks_strict="0", + pr_release_on_merge="1", + ) + + exit_code = mod._run_pr(workspace, workspace, args) + assert exit_code == 0 + assert commands + assert commands[0][:4] == [ + "python", + "scripts/github/pr_manager.py", + "--repo-root", + str(workspace), + ] + + +def test_run_pr_uses_make_for_non_root_repo( + monkeypatch: pytest.MonkeyPatch, + tmp_path: Path, +) -> None: + mod = _load_module("pr_workspace_project_command", "scripts/github/pr_workspace.py") + workspace = tmp_path / "workspace" + repo = workspace / "flext-core" + _ = repo.mkdir(parents=True) + + commands: list[list[str]] = [] + + def _fake_run(command: list[str], **_kwargs: Any) -> Any: + commands.append(command) + return SimpleNamespace(returncode=0) + + monkeypatch.setattr(mod.subprocess, "run", _fake_run) + + args = mod.argparse.Namespace( + pr_action="status", + pr_base="main", + pr_head="", + pr_number="", + pr_title="", + pr_body="", + pr_draft="0", + pr_merge_method="squash", + pr_auto="0", + pr_delete_branch="0", + pr_checks_strict="0", + pr_release_on_merge="1", + ) + + exit_code = mod._run_pr(repo, workspace, args) + assert exit_code == 0 + assert commands + assert commands[0][:4] == ["make", "-C", str(repo), "pr"] From e22606418be669a38e48abe30726124ad5b8404a Mon Sep 17 00:00:00 2001 From: Marlon Costa Date: Fri, 20 Feb 2026 16:03:27 -0300 Subject: [PATCH 29/34] chore: checkpoint pending 0.11.0-dev changes --- flexcore | 2 +- flext-api | 2 +- flext-auth | 2 +- flext-cli | 2 +- flext-core | 2 +- flext-db-oracle | 2 +- flext-dbt-ldap | 2 +- flext-dbt-ldif | 2 +- flext-dbt-oracle | 2 +- flext-dbt-oracle-wms | 2 +- flext-grpc | 2 +- flext-ldap | 2 +- flext-ldif | 2 +- flext-tap-oracle | 2 +- flext-tap-oracle-oic | 2 +- flext-tap-oracle-wms | 2 +- flext-target-ldap | 2 +- flext-target-ldif | 2 +- flext-target-oracle | 2 +- flext-target-oracle-oic | 2 +- flext-target-oracle-wms | 2 +- flext-web | 2 +- poetry.lock | 58 ++++++++++++++++++++--------------------- 23 files changed, 51 insertions(+), 51 deletions(-) diff --git a/flexcore b/flexcore index 0b17af00..3af103b8 160000 --- a/flexcore +++ b/flexcore @@ -1 +1 @@ -Subproject commit 0b17af00ea4d96b27f96c40c670bd21341aa2052 +Subproject commit 3af103b8a33250c6dbcce9ea481c5374d6d99d88 diff --git a/flext-api b/flext-api index 5b73dda9..a3b448c9 160000 --- a/flext-api +++ b/flext-api @@ -1 +1 @@ -Subproject commit 5b73dda9ac578070f92066a0aab1b32d286eea49 +Subproject commit a3b448c90b8dcc5ebeb05e49d8243042f62f72ba diff --git a/flext-auth b/flext-auth index a0293feb..f932d482 160000 --- a/flext-auth +++ b/flext-auth @@ -1 +1 @@ -Subproject commit a0293feba246775e2c7cdb7556a1179a108f1ae1 +Subproject commit f932d482af9771ca182f1308996e579b26368506 diff --git a/flext-cli b/flext-cli index b8f36721..a48c59c0 160000 --- a/flext-cli +++ b/flext-cli @@ -1 +1 @@ -Subproject commit b8f36721d2dacc148d02050281bff0f8f9a51019 +Subproject commit a48c59c0243ab1ad1345184b465c87b06c2a9413 diff --git a/flext-core b/flext-core index 587cc629..ffc23483 160000 --- a/flext-core +++ b/flext-core @@ -1 +1 @@ -Subproject commit 587cc629a9627eccdf0777501bb60d106f0e174d +Subproject commit ffc23483de5b953a69bf73d9bb8b6ea9e275b0ad diff --git a/flext-db-oracle b/flext-db-oracle index 4070a079..4cd0b0ce 160000 --- a/flext-db-oracle +++ b/flext-db-oracle @@ -1 +1 @@ -Subproject commit 4070a07905eed7fff27dcbb4ef45edcb8ba90691 +Subproject commit 4cd0b0ce7a362d1e7ab041d6135da2a9f5817ba3 diff --git a/flext-dbt-ldap b/flext-dbt-ldap index dc55e8fc..2a291660 160000 --- a/flext-dbt-ldap +++ b/flext-dbt-ldap @@ -1 +1 @@ -Subproject commit dc55e8fcec95449b25447d0e374a4f6a9db46e57 +Subproject commit 2a291660d90b13a725169fbb93836f095d278069 diff --git a/flext-dbt-ldif b/flext-dbt-ldif index e7c83a1a..f9cc8e0f 160000 --- a/flext-dbt-ldif +++ b/flext-dbt-ldif @@ -1 +1 @@ -Subproject commit e7c83a1accd3544eb7d8855e8c5411ebc98f68f8 +Subproject commit f9cc8e0f67e20d5b15f255acdbf808d23d15c9d9 diff --git a/flext-dbt-oracle b/flext-dbt-oracle index 5f4b0b7e..960c01c1 160000 --- a/flext-dbt-oracle +++ b/flext-dbt-oracle @@ -1 +1 @@ -Subproject commit 5f4b0b7e4ea89a1c462960a40f070122c4ba0510 +Subproject commit 960c01c115ff21339d03dee173afbc4ec435f579 diff --git a/flext-dbt-oracle-wms b/flext-dbt-oracle-wms index 2ca3ea48..91c8da9a 160000 --- a/flext-dbt-oracle-wms +++ b/flext-dbt-oracle-wms @@ -1 +1 @@ -Subproject commit 2ca3ea4844f4bf569c56527c74b904b457905ac8 +Subproject commit 91c8da9a63080c0c6b739d06ceb2b8000d786276 diff --git a/flext-grpc b/flext-grpc index e2225e29..3b74c77f 160000 --- a/flext-grpc +++ b/flext-grpc @@ -1 +1 @@ -Subproject commit e2225e29d20764d21b578c19993be0e4a7baed43 +Subproject commit 3b74c77fb31df27d8d0787e8e861dc0f8762c395 diff --git a/flext-ldap b/flext-ldap index 50896ab6..4330a0ce 160000 --- a/flext-ldap +++ b/flext-ldap @@ -1 +1 @@ -Subproject commit 50896ab6a002645d647bd0243971eebee69ec3cc +Subproject commit 4330a0ce1cbfd0ed0f292df533b5641cae1af78b diff --git a/flext-ldif b/flext-ldif index 7a014cc2..61a18324 160000 --- a/flext-ldif +++ b/flext-ldif @@ -1 +1 @@ -Subproject commit 7a014cc27c1ab442adfeab8594b8f11bb0fa66c4 +Subproject commit 61a1832446cf40a66e053a2d251399c1a6e9f29e diff --git a/flext-tap-oracle b/flext-tap-oracle index e56bbf3e..d141be75 160000 --- a/flext-tap-oracle +++ b/flext-tap-oracle @@ -1 +1 @@ -Subproject commit e56bbf3e9b13d7250d008e5b505a0b9c68e8da2b +Subproject commit d141be75092c7672a9cf4336fa8a2681ed209d9e diff --git a/flext-tap-oracle-oic b/flext-tap-oracle-oic index 2d9e2b25..96bf192f 160000 --- a/flext-tap-oracle-oic +++ b/flext-tap-oracle-oic @@ -1 +1 @@ -Subproject commit 2d9e2b250aa0736ff0f9b81ebdb241c876753002 +Subproject commit 96bf192ff246ef2d4733f28b3e3a665ca08d42b4 diff --git a/flext-tap-oracle-wms b/flext-tap-oracle-wms index d07b3e1a..a5d389f1 160000 --- a/flext-tap-oracle-wms +++ b/flext-tap-oracle-wms @@ -1 +1 @@ -Subproject commit d07b3e1a5b3763a4c913d6b5ab67b6f244797525 +Subproject commit a5d389f1946e324e54dd5617fa9b45ae78380102 diff --git a/flext-target-ldap b/flext-target-ldap index 82637e20..a6ac9dfb 160000 --- a/flext-target-ldap +++ b/flext-target-ldap @@ -1 +1 @@ -Subproject commit 82637e205c2d2ae82b51b5effbeac6ef9944d3df +Subproject commit a6ac9dfb2e30fa0ef16321520fa9600d3334c62a diff --git a/flext-target-ldif b/flext-target-ldif index d91feb0c..5be64829 160000 --- a/flext-target-ldif +++ b/flext-target-ldif @@ -1 +1 @@ -Subproject commit d91feb0ca079166896279bce6b512aac4d589453 +Subproject commit 5be64829d5d7ded2fe0730ca6fed47189d2c3156 diff --git a/flext-target-oracle b/flext-target-oracle index 7e65c69c..c3620df9 160000 --- a/flext-target-oracle +++ b/flext-target-oracle @@ -1 +1 @@ -Subproject commit 7e65c69c801f3ffb9ce12b9216b6b84dd8373caf +Subproject commit c3620df946be798fa4af3682b535853890d6cfa3 diff --git a/flext-target-oracle-oic b/flext-target-oracle-oic index 22affd73..85f1a0aa 160000 --- a/flext-target-oracle-oic +++ b/flext-target-oracle-oic @@ -1 +1 @@ -Subproject commit 22affd73c653e4e489c91f20e03b90c81263d3d7 +Subproject commit 85f1a0aad3e993637c339e285a967287508fa8b4 diff --git a/flext-target-oracle-wms b/flext-target-oracle-wms index 9c9ac660..3abb4f6e 160000 --- a/flext-target-oracle-wms +++ b/flext-target-oracle-wms @@ -1 +1 @@ -Subproject commit 9c9ac6604e45e94ddc894008f2fb43bfd7940af1 +Subproject commit 3abb4f6e613c585e09f1f2c3b866ccfedba834ff diff --git a/flext-web b/flext-web index a41219bc..82461042 160000 --- a/flext-web +++ b/flext-web @@ -1 +1 @@ -Subproject commit a41219bc03b7c42a6c53e1f8a191f1443a966328 +Subproject commit 82461042d72c9a285c9c041376336e3f7f49c3d7 diff --git a/poetry.lock b/poetry.lock index a1654ca4..d5113c09 100644 --- a/poetry.lock +++ b/poetry.lock @@ -2520,7 +2520,7 @@ dotenv = ["python-dotenv"] [[package]] name = "flexcore" -version = "0.10.0-dev" +version = "0.10.0" description = "FlexCore - High-Performance Go-Python Hybrid Enterprise Framework" optional = false python-versions = ">=3.13,<3.14" @@ -2539,7 +2539,7 @@ url = "flexcore" [[package]] name = "flext-api" -version = "0.10.0-dev" +version = "0.10.0" description = "FLEXT API - High-Performance REST API with FastAPI" optional = false python-versions = ">=3.13,<3.14" @@ -2574,7 +2574,7 @@ url = "flext-api" [[package]] name = "flext-auth" -version = "0.10.0-dev" +version = "0.10.0" description = "FLEXT Auth - Enterprise Authentication & Authorization Service" optional = false python-versions = ">=3.13,<3.14" @@ -2602,7 +2602,7 @@ url = "flext-auth" [[package]] name = "flext-cli" -version = "0.10.0-dev" +version = "0.10.0" description = "FLEXT CLI - Developer Command Line Interface" optional = false python-versions = ">=3.13,<3.14" @@ -2669,7 +2669,7 @@ url = "flext-core" [[package]] name = "flext-db-oracle" -version = "0.10.0-dev" +version = "0.10.0" description = "FLEXT DB Oracle - Enterprise Oracle Database Operations Library" optional = false python-versions = ">=3.13,<3.14" @@ -2688,7 +2688,7 @@ url = "flext-db-oracle" [[package]] name = "flext-dbt-ldap" -version = "0.10.0-dev" +version = "0.10.0" description = "FLEXT dbt LDAP - dbt Models for LDAP Data Transformation" optional = false python-versions = ">=3.13,<3.14" @@ -2706,7 +2706,7 @@ url = "flext-dbt-ldap" [[package]] name = "flext-dbt-ldif" -version = "0.10.0-dev" +version = "0.10.0" description = "FLEXT dbt LDAP - dbt Models for LDIF Data Transformation" optional = false python-versions = ">=3.13,<3.14" @@ -2724,7 +2724,7 @@ url = "flext-dbt-ldif" [[package]] name = "flext-dbt-oracle" -version = "0.10.0-dev" +version = "0.10.0" description = "FLEXT dbt Oracle - dbt Models for Oracle Database" optional = false python-versions = ">=3.13,<3.14" @@ -2744,7 +2744,7 @@ url = "flext-dbt-oracle" [[package]] name = "flext-dbt-oracle-wms" -version = "0.10.0-dev" +version = "0.10.0" description = "FLEXT DBT Oracle WMS - Oracle WMS data transformation with DBT" optional = false python-versions = ">=3.13,<3.14" @@ -2763,7 +2763,7 @@ url = "flext-dbt-oracle-wms" [[package]] name = "flext-grpc" -version = "0.10.0-dev" +version = "0.10.0" description = "FLEXT gRPC - High-Performance gRPC Services" optional = false python-versions = ">=3.13,<3.14" @@ -2794,7 +2794,7 @@ url = "flext-grpc" [[package]] name = "flext-ldap" -version = "0.10.0-dev" +version = "0.10.0" description = "Enterprise LDAP Operations Library for FLEXT Framework" optional = false python-versions = ">=3.13,<3.14" @@ -2817,7 +2817,7 @@ url = "flext-ldap" [[package]] name = "flext-ldif" -version = "0.10.0-dev" +version = "0.10.0" description = "FLEXT LDIF - Enterprise LDIF Processing Library" optional = false python-versions = ">=3.13,<3.14" @@ -2840,7 +2840,7 @@ url = "flext-ldif" [[package]] name = "flext-meltano" -version = "0.10.0-dev" +version = "0.10.0" description = "FLEXT Meltano - Enterprise Data Integration Platform" optional = false python-versions = ">=3.13,<3.14" @@ -2879,7 +2879,7 @@ url = "flext-meltano" [[package]] name = "flext-observability" -version = "0.10.0-dev" +version = "0.10.0" description = "FLEXT Observability - Enterprise Monitoring, Metrics & Telemetry" optional = false python-versions = ">=3.13,<3.14" @@ -2916,7 +2916,7 @@ url = "flext-observability" [[package]] name = "flext-oracle-oic" -version = "0.10.0-dev" +version = "0.10.0" description = "FLEXT Oracle OIC Extension - Advanced Oracle Integration Cloud Extensions" optional = false python-versions = ">=3.13,<3.14" @@ -2941,7 +2941,7 @@ url = "flext-oracle-oic" [[package]] name = "flext-oracle-wms" -version = "0.10.0-dev" +version = "0.10.0" description = "Enterprise Oracle WMS client library for FLEXT data integration platform" optional = false python-versions = ">=3.13,<3.14" @@ -2961,7 +2961,7 @@ url = "flext-oracle-wms" [[package]] name = "flext-plugin" -version = "0.10.0-dev" +version = "0.10.0" description = "FLEXT Plugin - Plugin System for FLEXT Platform" optional = false python-versions = ">=3.13,<3.14" @@ -2990,7 +2990,7 @@ url = "flext-plugin" [[package]] name = "flext-quality" -version = "0.10.0-dev" +version = "0.10.0" description = "FLEXT Quality - Unified orchestration platform for Claude Code tooling" optional = false python-versions = ">=3.13,<3.14" @@ -3025,7 +3025,7 @@ url = "flext-quality" [[package]] name = "flext-tap-ldap" -version = "0.10.0-dev" +version = "0.10.0" description = "FLEXT Tap LDAP - Singer Tap for LDAP Directory Services" optional = false python-versions = ">=3.13,<3.14" @@ -3043,7 +3043,7 @@ url = "flext-tap-ldap" [[package]] name = "flext-tap-ldif" -version = "0.10.0-dev" +version = "0.10.0" description = "FLEXT Tap LDIF - Singer Tap for LDIF file format data extraction" optional = false python-versions = ">=3.13,<3.14" @@ -3065,7 +3065,7 @@ url = "flext-tap-ldif" [[package]] name = "flext-tap-oracle" -version = "0.10.0-dev" +version = "0.10.0" description = "FLEXT Tap Oracle - Modern Singer Tap for Oracle Database" optional = false python-versions = ">=3.13,<3.14" @@ -3092,7 +3092,7 @@ url = "flext-tap-oracle" [[package]] name = "flext-tap-oracle-oic" -version = "0.10.0-dev" +version = "0.10.0" description = "FLEXT Tap Oracle OIC - Singer Tap for Oracle Integration Cloud" optional = false python-versions = ">=3.13,<3.14" @@ -3112,7 +3112,7 @@ url = "flext-tap-oracle-oic" [[package]] name = "flext-tap-oracle-wms" -version = "0.10.0-dev" +version = "0.10.0" description = "FLEXT Tap Oracle WMS - Singer Tap for Oracle Warehouse Management System" optional = false python-versions = ">=3.13,<3.14" @@ -3132,7 +3132,7 @@ url = "flext-tap-oracle-wms" [[package]] name = "flext-target-ldap" -version = "0.10.0-dev" +version = "0.10.0" description = "FLEXT Target for LDAP directory loading" optional = false python-versions = ">=3.13,<3.14" @@ -3152,7 +3152,7 @@ url = "flext-target-ldap" [[package]] name = "flext-target-ldif" -version = "0.10.0-dev" +version = "0.10.0" description = "FLEXT Target LDIF - Singer Target for LDAP Data Interchange Format (LDIF) output" optional = false python-versions = ">=3.13,<3.14" @@ -3175,7 +3175,7 @@ url = "flext-target-ldif" [[package]] name = "flext-target-oracle" -version = "0.10.0-dev" +version = "0.10.0" description = "FLEXT Target Oracle - Singer Target for Oracle Database Data Loading" optional = false python-versions = ">=3.13,<3.14" @@ -3201,7 +3201,7 @@ url = "flext-target-oracle" [[package]] name = "flext-target-oracle-oic" -version = "0.10.0-dev" +version = "0.10.0" description = "FLEXT Target Oracle OIC - Singer Target for Oracle Integration Cloud" optional = false python-versions = ">=3.13,<3.14" @@ -3230,7 +3230,7 @@ url = "flext-target-oracle-oic" [[package]] name = "flext-target-oracle-wms" -version = "0.10.0-dev" +version = "0.10.0" description = "FLEXT Target Oracle WMS - Singer Target for Oracle WMS Data" optional = false python-versions = ">=3.13,<3.14" @@ -3256,7 +3256,7 @@ url = "flext-target-oracle-wms" [[package]] name = "flext-web" -version = "0.10.0-dev" +version = "0.10.0" description = "FLEXT Web - Modern Web Interface for FLEXT Platform" optional = false python-versions = ">=3.13,<3.14" From f5e1240857b08afac935edb147e01368d020532b Mon Sep 17 00:00:00 2001 From: Marlon Costa Date: Fri, 20 Feb 2026 16:05:54 -0300 Subject: [PATCH 30/34] chore: checkpoint pending 0.11.0-dev changes --- flext-ldif | 2 +- flext-meltano | 2 +- flext-observability | 2 +- flext-oracle-oic | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/flext-ldif b/flext-ldif index 61a18324..b3e50ac0 160000 --- a/flext-ldif +++ b/flext-ldif @@ -1 +1 @@ -Subproject commit 61a1832446cf40a66e053a2d251399c1a6e9f29e +Subproject commit b3e50ac0c8f3e46c27375ee254c7689e593144b4 diff --git a/flext-meltano b/flext-meltano index 92052e97..915e5b82 160000 --- a/flext-meltano +++ b/flext-meltano @@ -1 +1 @@ -Subproject commit 92052e97da161a92d6ecb81fbdfc79712e158624 +Subproject commit 915e5b824c752ceb96c48d387ed0b1b30ffb29f4 diff --git a/flext-observability b/flext-observability index f2a76201..5933ffdb 160000 --- a/flext-observability +++ b/flext-observability @@ -1 +1 @@ -Subproject commit f2a76201cfc070e7c845a8729793fcf875a9d7c9 +Subproject commit 5933ffdbd83ca3b5be6ebd49b84c03dfa4b004a8 diff --git a/flext-oracle-oic b/flext-oracle-oic index 21ca240a..5b47321b 160000 --- a/flext-oracle-oic +++ b/flext-oracle-oic @@ -1 +1 @@ -Subproject commit 21ca240a4f8cf71b6d24dcb195741b958ebc83f9 +Subproject commit 5b47321b563979ff877ad75451361f8b2f4bc535 From 38e903551d8134b85c277a44a77097e62a314609 Mon Sep 17 00:00:00 2001 From: Marlon Costa Date: Fri, 20 Feb 2026 16:10:06 -0300 Subject: [PATCH 31/34] chore: checkpoint pending 0.11.0-dev changes --- flext-ldif | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flext-ldif b/flext-ldif index b3e50ac0..c472602d 160000 --- a/flext-ldif +++ b/flext-ldif @@ -1 +1 @@ -Subproject commit b3e50ac0c8f3e46c27375ee254c7689e593144b4 +Subproject commit c472602d2774c36619231b7e0bd2ed43d9a11392 From 892ac3929ed6424b370dcb3c6b8f85a420da2092 Mon Sep 17 00:00:00 2001 From: Marlon Costa Date: Fri, 20 Feb 2026 16:44:35 -0300 Subject: [PATCH 32/34] chore(workspace): checkpoint flext-ldif pending updates --- flext-ldif | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flext-ldif b/flext-ldif index c472602d..5ee93e3f 160000 --- a/flext-ldif +++ b/flext-ldif @@ -1 +1 @@ -Subproject commit c472602d2774c36619231b7e0bd2ed43d9a11392 +Subproject commit 5ee93e3ff75f4fb637e082dd0f2d03c6b8a58bc9 From 784ac557f857f505e9b3600407c7390b3d0e4b4e Mon Sep 17 00:00:00 2001 From: Marlon Costa Date: Fri, 20 Feb 2026 17:11:11 -0300 Subject: [PATCH 33/34] chore: checkpoint pending 0.11.0-dev changes --- Makefile | 4 + flexcore | 2 +- flext-api | 2 +- flext-auth | 2 +- flext-cli | 2 +- flext-core | 2 +- flext-db-oracle | 2 +- flext-dbt-ldap | 2 +- flext-dbt-ldif | 2 +- flext-dbt-oracle | 2 +- flext-dbt-oracle-wms | 2 +- flext-grpc | 2 +- flext-ldap | 2 +- flext-ldif | 2 +- flext-meltano | 2 +- flext-observability | 2 +- flext-oracle-oic | 2 +- flext-oracle-wms | 2 +- flext-plugin | 2 +- flext-quality | 2 +- flext-tap-ldap | 2 +- flext-tap-ldif | 2 +- flext-tap-oracle | 2 +- flext-tap-oracle-oic | 2 +- flext-tap-oracle-wms | 2 +- flext-target-ldap | 2 +- flext-target-ldif | 2 +- flext-target-oracle | 2 +- flext-target-oracle-oic | 2 +- flext-target-oracle-wms | 2 +- flext-web | 2 +- libs/versioning.py | 73 +++++++++++++++++++ scripts/github/pr_manager.py | 21 ++++-- scripts/release/run.py | 30 ++++---- scripts/release/shared.py | 26 +------ scripts/release/version.py | 34 ++------- scripts/sync.py | 9 ++- tests/unit/libs/versioning_tests.py | 63 ++++++++++++++++ tests/unit/scripts/github/pr_manager_tests.py | 34 +++++++++ .../release/release_shared_and_run_tests.py | 25 +++++++ tests/unit/scripts/sync_tests.py | 70 ++++++++++++++++++ 41 files changed, 347 insertions(+), 102 deletions(-) create mode 100644 libs/versioning.py create mode 100644 tests/unit/libs/versioning_tests.py create mode 100644 tests/unit/scripts/sync_tests.py diff --git a/Makefile b/Makefile index e3acc1b2..1b34e50e 100644 --- a/Makefile +++ b/Makefile @@ -22,6 +22,7 @@ PUSH ?= VERSION ?= TAG ?= BUMP ?= +RELEASE_DEV_SUFFIX ?= 0 CREATE_BRANCHES ?= 1 PR_ACTION ?= status PR_BASE ?= main @@ -175,6 +176,7 @@ help: ## Show simple workspace verbs $(Q)echo " DRY_RUN=1 Print plan, do not tag/push" $(Q)echo " PUSH=1 Push release commit/tag" $(Q)echo " VERSION= TAG=v BUMP=patch Release controls" + $(Q)echo " RELEASE_DEV_SUFFIX=0|1 Append -dev during release version phase" $(Q)echo " CREATE_BRANCHES=1|0 Create release branches in workspace + projects" $(Q)echo " PR_ACTION=status|create|view|checks|merge|close" $(Q)echo " PR_BASE=main PR_HEAD= PR_NUMBER= PR_DRAFT=0|1" @@ -436,6 +438,7 @@ release: ## Interactive workspace release orchestration --root "$(CURDIR)" \ --phase "$(RELEASE_PHASE)" \ --interactive "$(INTERACTIVE)" \ + --dev-suffix "$(RELEASE_DEV_SUFFIX)" \ --create-branches "$(CREATE_BRANCHES)" \ --projects $(SELECTED_PROJECTS) \ $(if $(DRY_RUN),--dry-run "$(DRY_RUN)",) \ @@ -453,6 +456,7 @@ release-ci: ## Non-interactive release run for CI/tag workflows --root "$(CURDIR)" \ --phase "$(RELEASE_PHASE)" \ --interactive 0 \ + --dev-suffix "$(RELEASE_DEV_SUFFIX)" \ --create-branches 0 \ --projects $(SELECTED_PROJECTS) \ $(if $(DRY_RUN),--dry-run "$(DRY_RUN)",) \ diff --git a/flexcore b/flexcore index 3af103b8..8f841ad9 160000 --- a/flexcore +++ b/flexcore @@ -1 +1 @@ -Subproject commit 3af103b8a33250c6dbcce9ea481c5374d6d99d88 +Subproject commit 8f841ad9780686803f403dcaee96bbc25c732a57 diff --git a/flext-api b/flext-api index a3b448c9..c26cc5fe 160000 --- a/flext-api +++ b/flext-api @@ -1 +1 @@ -Subproject commit a3b448c90b8dcc5ebeb05e49d8243042f62f72ba +Subproject commit c26cc5fe1fef4517e92909c50ddc1684f95c19e8 diff --git a/flext-auth b/flext-auth index f932d482..2205b4ae 160000 --- a/flext-auth +++ b/flext-auth @@ -1 +1 @@ -Subproject commit f932d482af9771ca182f1308996e579b26368506 +Subproject commit 2205b4ae2877e1e3cdc0c8aa4cf8a9fbf86ff654 diff --git a/flext-cli b/flext-cli index a48c59c0..a48909a6 160000 --- a/flext-cli +++ b/flext-cli @@ -1 +1 @@ -Subproject commit a48c59c0243ab1ad1345184b465c87b06c2a9413 +Subproject commit a48909a6fa852b843dc642ac5445a7a1b6def49f diff --git a/flext-core b/flext-core index ffc23483..ecebd172 160000 --- a/flext-core +++ b/flext-core @@ -1 +1 @@ -Subproject commit ffc23483de5b953a69bf73d9bb8b6ea9e275b0ad +Subproject commit ecebd1725b8196a1f1858f2a5da7595df8bd26e9 diff --git a/flext-db-oracle b/flext-db-oracle index 4cd0b0ce..a28cd6eb 160000 --- a/flext-db-oracle +++ b/flext-db-oracle @@ -1 +1 @@ -Subproject commit 4cd0b0ce7a362d1e7ab041d6135da2a9f5817ba3 +Subproject commit a28cd6ebd201f7db1feed49c176cf89b53706356 diff --git a/flext-dbt-ldap b/flext-dbt-ldap index 2a291660..d4be39fc 160000 --- a/flext-dbt-ldap +++ b/flext-dbt-ldap @@ -1 +1 @@ -Subproject commit 2a291660d90b13a725169fbb93836f095d278069 +Subproject commit d4be39fc598434a78b1b3857302378d7eea1fe22 diff --git a/flext-dbt-ldif b/flext-dbt-ldif index f9cc8e0f..a60b0a86 160000 --- a/flext-dbt-ldif +++ b/flext-dbt-ldif @@ -1 +1 @@ -Subproject commit f9cc8e0f67e20d5b15f255acdbf808d23d15c9d9 +Subproject commit a60b0a8629fe8a5681e1b06e20155556307d3405 diff --git a/flext-dbt-oracle b/flext-dbt-oracle index 960c01c1..a9d377a6 160000 --- a/flext-dbt-oracle +++ b/flext-dbt-oracle @@ -1 +1 @@ -Subproject commit 960c01c115ff21339d03dee173afbc4ec435f579 +Subproject commit a9d377a69e4c1d6c553b5408d22442bb25e4a504 diff --git a/flext-dbt-oracle-wms b/flext-dbt-oracle-wms index 91c8da9a..e7e4df75 160000 --- a/flext-dbt-oracle-wms +++ b/flext-dbt-oracle-wms @@ -1 +1 @@ -Subproject commit 91c8da9a63080c0c6b739d06ceb2b8000d786276 +Subproject commit e7e4df755fca87a894e32e11467148774f3ad772 diff --git a/flext-grpc b/flext-grpc index 3b74c77f..611259da 160000 --- a/flext-grpc +++ b/flext-grpc @@ -1 +1 @@ -Subproject commit 3b74c77fb31df27d8d0787e8e861dc0f8762c395 +Subproject commit 611259da6f5358b2f4e64f1e1a44bd7867c8fe9f diff --git a/flext-ldap b/flext-ldap index 4330a0ce..07739926 160000 --- a/flext-ldap +++ b/flext-ldap @@ -1 +1 @@ -Subproject commit 4330a0ce1cbfd0ed0f292df533b5641cae1af78b +Subproject commit 0773992654670d7b74310a32bec71393b00049b9 diff --git a/flext-ldif b/flext-ldif index 5ee93e3f..63f959b7 160000 --- a/flext-ldif +++ b/flext-ldif @@ -1 +1 @@ -Subproject commit 5ee93e3ff75f4fb637e082dd0f2d03c6b8a58bc9 +Subproject commit 63f959b715a57b6900f41a3d79f9a9a1a3cf7f5c diff --git a/flext-meltano b/flext-meltano index 915e5b82..144bb0ce 160000 --- a/flext-meltano +++ b/flext-meltano @@ -1 +1 @@ -Subproject commit 915e5b824c752ceb96c48d387ed0b1b30ffb29f4 +Subproject commit 144bb0cef987909da40ec16d7dcfe0ad2d690e63 diff --git a/flext-observability b/flext-observability index 5933ffdb..036dc83c 160000 --- a/flext-observability +++ b/flext-observability @@ -1 +1 @@ -Subproject commit 5933ffdbd83ca3b5be6ebd49b84c03dfa4b004a8 +Subproject commit 036dc83c24eada8ee87dac8cc6adab1a06c12b8b diff --git a/flext-oracle-oic b/flext-oracle-oic index 5b47321b..b2530a1a 160000 --- a/flext-oracle-oic +++ b/flext-oracle-oic @@ -1 +1 @@ -Subproject commit 5b47321b563979ff877ad75451361f8b2f4bc535 +Subproject commit b2530a1a14e48d84acc87a91b013a58db4af9ef1 diff --git a/flext-oracle-wms b/flext-oracle-wms index 1313f288..624cbd24 160000 --- a/flext-oracle-wms +++ b/flext-oracle-wms @@ -1 +1 @@ -Subproject commit 1313f2884396f937de580815c66167b07e9a3a3b +Subproject commit 624cbd24158d7a0ac82242d6aea47f024cc8700e diff --git a/flext-plugin b/flext-plugin index 4e344862..5ce0a1fa 160000 --- a/flext-plugin +++ b/flext-plugin @@ -1 +1 @@ -Subproject commit 4e3448629adabc802a7036adda82ccedbcc0e711 +Subproject commit 5ce0a1fa5dad3bcc36119e9666f1b81a362aa509 diff --git a/flext-quality b/flext-quality index fad455b5..1a66af6b 160000 --- a/flext-quality +++ b/flext-quality @@ -1 +1 @@ -Subproject commit fad455b51ec0607cdc524ed0ef72daddf30f2542 +Subproject commit 1a66af6bfed4fe10902c10259059681917ce1d94 diff --git a/flext-tap-ldap b/flext-tap-ldap index f3528f07..acdb8160 160000 --- a/flext-tap-ldap +++ b/flext-tap-ldap @@ -1 +1 @@ -Subproject commit f3528f074643aab5264c0230a4a40fa21a8c9fe0 +Subproject commit acdb81601645c3b3757f4d10d5692bca911ea084 diff --git a/flext-tap-ldif b/flext-tap-ldif index 25debf8b..02f844ae 160000 --- a/flext-tap-ldif +++ b/flext-tap-ldif @@ -1 +1 @@ -Subproject commit 25debf8b6740813b818306e99e7d8f584695c0e9 +Subproject commit 02f844aedca8664ad02cf6e2b030c71c7127ad71 diff --git a/flext-tap-oracle b/flext-tap-oracle index d141be75..bd27abc2 160000 --- a/flext-tap-oracle +++ b/flext-tap-oracle @@ -1 +1 @@ -Subproject commit d141be75092c7672a9cf4336fa8a2681ed209d9e +Subproject commit bd27abc2fb3a65b9ec666d83b86f3ea73f7bd5b5 diff --git a/flext-tap-oracle-oic b/flext-tap-oracle-oic index 96bf192f..a1e6d20a 160000 --- a/flext-tap-oracle-oic +++ b/flext-tap-oracle-oic @@ -1 +1 @@ -Subproject commit 96bf192ff246ef2d4733f28b3e3a665ca08d42b4 +Subproject commit a1e6d20ae6e9d9f0e4330b3090b385ac0a798877 diff --git a/flext-tap-oracle-wms b/flext-tap-oracle-wms index a5d389f1..dfd3ff4e 160000 --- a/flext-tap-oracle-wms +++ b/flext-tap-oracle-wms @@ -1 +1 @@ -Subproject commit a5d389f1946e324e54dd5617fa9b45ae78380102 +Subproject commit dfd3ff4e07bcb16668158af320f3ec1e4c844201 diff --git a/flext-target-ldap b/flext-target-ldap index a6ac9dfb..7866ff73 160000 --- a/flext-target-ldap +++ b/flext-target-ldap @@ -1 +1 @@ -Subproject commit a6ac9dfb2e30fa0ef16321520fa9600d3334c62a +Subproject commit 7866ff73dc1f6006d1f9ed3faeaf7c944bdf6f72 diff --git a/flext-target-ldif b/flext-target-ldif index 5be64829..cd6fd3a5 160000 --- a/flext-target-ldif +++ b/flext-target-ldif @@ -1 +1 @@ -Subproject commit 5be64829d5d7ded2fe0730ca6fed47189d2c3156 +Subproject commit cd6fd3a5e6c32b6ccf64ad37ccb2ac0766179bb1 diff --git a/flext-target-oracle b/flext-target-oracle index c3620df9..6e07187b 160000 --- a/flext-target-oracle +++ b/flext-target-oracle @@ -1 +1 @@ -Subproject commit c3620df946be798fa4af3682b535853890d6cfa3 +Subproject commit 6e07187b55e24c13dc792851724594d4db219bfa diff --git a/flext-target-oracle-oic b/flext-target-oracle-oic index 85f1a0aa..1304beb3 160000 --- a/flext-target-oracle-oic +++ b/flext-target-oracle-oic @@ -1 +1 @@ -Subproject commit 85f1a0aad3e993637c339e285a967287508fa8b4 +Subproject commit 1304beb330da1c79fdc7694e2db9dfa8e5af5669 diff --git a/flext-target-oracle-wms b/flext-target-oracle-wms index 3abb4f6e..a1564f1c 160000 --- a/flext-target-oracle-wms +++ b/flext-target-oracle-wms @@ -1 +1 @@ -Subproject commit 3abb4f6e613c585e09f1f2c3b866ccfedba834ff +Subproject commit a1564f1ca7760eb3da1a792f9afd08dc74ddb6a2 diff --git a/flext-web b/flext-web index 82461042..2fba7fa5 160000 --- a/flext-web +++ b/flext-web @@ -1 +1 @@ -Subproject commit 82461042d72c9a285c9c041376336e3f7f49c3d7 +Subproject commit 2fba7fa504b1a435e37d413478718dc027960b01 diff --git a/libs/versioning.py b/libs/versioning.py new file mode 100644 index 00000000..a5e0cfb3 --- /dev/null +++ b/libs/versioning.py @@ -0,0 +1,73 @@ +from __future__ import annotations + +import re +import tomllib +from pathlib import Path + +import tomlkit +from tomlkit.items import Table + + +SEMVER_RE = re.compile( + r"^(?P0|[1-9]\d*)\.(?P0|[1-9]\d*)\.(?P0|[1-9]\d*)$" +) + + +def parse_semver(version: str) -> tuple[int, int, int]: + match = SEMVER_RE.match(version) + if not match: + raise ValueError(f"invalid semver version: {version}") + return ( + int(match.group("major")), + int(match.group("minor")), + int(match.group("patch")), + ) + + +def bump_version(current_version: str, bump: str) -> str: + major, minor, patch = parse_semver(current_version) + if bump == "major": + return f"{major + 1}.0.0" + if bump == "minor": + return f"{major}.{minor + 1}.0" + if bump == "patch": + return f"{major}.{minor}.{patch + 1}" + raise ValueError(f"unsupported bump: {bump}") + + +def release_tag_from_branch(branch: str) -> str | None: + version = branch.removesuffix("-dev") + if SEMVER_RE.fullmatch(version): + return f"v{version}" + match = re.fullmatch(r"release/(?P\d+\.\d+\.\d+)", branch) + if not match: + return None + return f"v{match.group('version')}" + + +def current_workspace_version(root: Path) -> str: + pyproject = root / "pyproject.toml" + data = tomllib.loads(pyproject.read_text(encoding="utf-8")) + project = data.get("project") + if not isinstance(project, dict): + raise RuntimeError("unable to detect [project] section from pyproject.toml") + version = project.get("version") + if not isinstance(version, str) or not version: + raise RuntimeError("unable to detect version from pyproject.toml") + return version.removesuffix("-dev") + + +def replace_project_version(content: str, version: str) -> tuple[str, bool]: + document = tomlkit.parse(content) + project = document.get("project") + if not isinstance(project, Table): + return content, False + current = project.get("version") + if not isinstance(current, str) or not current: + return content, False + _ = parse_semver(current.removesuffix("-dev")) + if current == version: + return content, False + project["version"] = version + updated = tomlkit.dumps(document) + return updated, updated != content diff --git a/scripts/github/pr_manager.py b/scripts/github/pr_manager.py index 2e1a2f77..12f9d17c 100644 --- a/scripts/github/pr_manager.py +++ b/scripts/github/pr_manager.py @@ -3,9 +3,16 @@ import argparse import json -import re import subprocess from pathlib import Path +import sys + + +REPO_ROOT = Path(__file__).resolve().parents[2] +if str(REPO_ROOT) not in sys.path: + sys.path.insert(0, str(REPO_ROOT)) + +from libs.versioning import release_tag_from_branch def _run_capture(command: list[str], cwd: Path) -> str: @@ -98,13 +105,7 @@ def _selector(pr_number: str, head: str) -> str: def _release_tag_from_head(head: str) -> str | None: - version = head.removesuffix("-dev") - if re.fullmatch(r"\d+\.\d+\.\d+", version): - return f"v{version}" - match = re.fullmatch(r"release/(?P\d+\.\d+\.\d+)", head) - if match: - return f"v{match.group('version')}" - return None + return release_tag_from_branch(head) def _is_workspace_release_repo(repo_root: Path) -> bool: @@ -177,6 +178,10 @@ def _merge_pr( delete_branch: int, release_on_merge: int, ) -> int: + if selector == head and _open_pr_for_head(repo_root, head) is None: + print("status=no-open-pr") + return 0 + command = ["gh", "pr", "merge", selector] merge_flag = { "merge": "--merge", diff --git a/scripts/release/run.py b/scripts/release/run.py index 36758acf..244819db 100644 --- a/scripts/release/run.py +++ b/scripts/release/run.py @@ -4,7 +4,6 @@ import argparse from pathlib import Path import sys -import tomllib SCRIPTS_ROOT = Path(__file__).resolve().parents[1] if str(SCRIPTS_ROOT) not in sys.path: @@ -18,6 +17,7 @@ run_checked, workspace_root, ) +from libs.versioning import current_workspace_version def _parse_args() -> argparse.Namespace: @@ -30,22 +30,14 @@ def _parse_args() -> argparse.Namespace: _ = parser.add_argument("--interactive", type=int, default=1) _ = parser.add_argument("--push", type=int, default=0) _ = parser.add_argument("--dry-run", type=int, default=0) + _ = parser.add_argument("--dev-suffix", type=int, default=0) _ = parser.add_argument("--create-branches", type=int, default=1) _ = parser.add_argument("--projects", nargs="*", default=[]) return parser.parse_args() def _current_version(root: Path) -> str: - pyproject = root / "pyproject.toml" - content = pyproject.read_bytes() - data = tomllib.loads(content.decode("utf-8")) - project = data.get("project") - if not isinstance(project, dict): - raise RuntimeError("unable to detect [project] section from pyproject.toml") - version = project.get("version") - if not isinstance(version, str) or not version: - raise RuntimeError("unable to detect version from pyproject.toml") - return version.removesuffix("-dev") + return current_workspace_version(root) def _resolve_version(args: argparse.Namespace, root: Path) -> str: @@ -85,7 +77,11 @@ def _create_release_branches( def _phase_version( - root: Path, version: str, dry_run: bool, project_names: list[str] + root: Path, + version: str, + dry_run: bool, + project_names: list[str], + dev_suffix: bool, ) -> None: command = [ "python", @@ -96,6 +92,8 @@ def _phase_version( version, "--check" if dry_run else "--apply", ] + if dev_suffix: + command.extend(["--dev-suffix", "1"]) if project_names: command.extend(["--projects", *project_names]) run_checked(command, cwd=root) @@ -199,7 +197,13 @@ def main() -> int: _phase_validate(root) continue if phase == "version": - _phase_version(root, version, args.dry_run == 1, selected_project_names) + _phase_version( + root, + version, + args.dry_run == 1, + selected_project_names, + args.dev_suffix == 1, + ) continue if phase == "build": _phase_build(root, version, selected_project_names) diff --git a/scripts/release/shared.py b/scripts/release/shared.py index a1abb23c..b191d49f 100644 --- a/scripts/release/shared.py +++ b/scripts/release/shared.py @@ -2,7 +2,6 @@ # Owner-Skill: .claude/skills/scripts-maintenance/SKILL.md from __future__ import annotations -import re import sys from pathlib import Path @@ -15,11 +14,8 @@ from libs.selection import resolve_projects as _resolve_projects from libs.subprocess import run_capture as _run_capture from libs.subprocess import run_checked as _run_checked - - -SEMVER_RE = re.compile( - r"^(?P0|[1-9]\d*)\.(?P0|[1-9]\d*)\.(?P0|[1-9]\d*)$" -) +from libs.versioning import bump_version as _bump_version +from libs.versioning import parse_semver as _parse_semver Project = ProjectInfo @@ -39,25 +35,11 @@ def resolve_projects(root: Path, names: list[str]) -> list[Project]: def parse_semver(version: str) -> tuple[int, int, int]: - match = SEMVER_RE.match(version) - if not match: - raise ValueError(f"invalid semver version: {version}") - return ( - int(match.group("major")), - int(match.group("minor")), - int(match.group("patch")), - ) + return _parse_semver(version) def bump_version(current_version: str, bump: str) -> str: - major, minor, patch = parse_semver(current_version) - if bump == "major": - return f"{major + 1}.0.0" - if bump == "minor": - return f"{major}.{minor + 1}.0" - if bump == "patch": - return f"{major}.{minor}.{patch + 1}" - raise ValueError(f"unsupported bump: {bump}") + return _bump_version(current_version, bump) def run_checked(command: list[str], cwd: Path | None = None) -> None: diff --git a/scripts/release/version.py b/scripts/release/version.py index 3851de0f..db382213 100644 --- a/scripts/release/version.py +++ b/scripts/release/version.py @@ -3,7 +3,6 @@ import argparse from pathlib import Path -import re import sys SCRIPTS_ROOT = Path(__file__).resolve().parents[1] @@ -11,34 +10,11 @@ sys.path.insert(0, str(SCRIPTS_ROOT)) from release.shared import parse_semver, resolve_projects, workspace_root +from libs.versioning import replace_project_version def _replace_version(content: str, version: str) -> tuple[str, bool]: - project_match = re.search(r"(?ms)^\[project\]\n(?P.*?)(?:^\[|\Z)", content) - if not project_match: - return content, False - - body = project_match.group("body") - version_match = re.search(r'(?m)^version\s*=\s*"(?P[^"]+)"\s*$', body) - if not version_match: - return content, False - - current = version_match.group("value") - current_clean = current.removesuffix("-dev") - _ = parse_semver(current_clean) - if current == version: - return content, False - - replacement = f'version = "{version}"' - updated_body = re.sub( - r'(?m)^version\s*=\s*"[^"]+"\s*$', - replacement, - body, - count=1, - ) - start, end = project_match.span("body") - updated = content[:start] + updated_body + content[end:] - return updated, updated != content + return replace_project_version(content, version) def _version_files(root: Path, project_names: list[str]) -> list[Path]: @@ -55,6 +31,7 @@ def _parse_args() -> argparse.Namespace: parser = argparse.ArgumentParser() _ = parser.add_argument("--root", type=Path, default=Path(".")) _ = parser.add_argument("--version", required=True) + _ = parser.add_argument("--dev-suffix", type=int, default=0) _ = parser.add_argument("--projects", nargs="*", default=[]) _ = parser.add_argument("--apply", action="store_true") _ = parser.add_argument("--check", action="store_true") @@ -65,11 +42,12 @@ def main() -> int: args = _parse_args() root = workspace_root(args.root) _ = parse_semver(args.version) + target_version = f"{args.version}-dev" if args.dev_suffix == 1 else args.version changed = 0 for file_path in _version_files(root, args.projects): content = file_path.read_text(encoding="utf-8") - updated, did_change = _replace_version(content, args.version) + updated, did_change = _replace_version(content, target_version) if did_change: changed += 1 if args.apply: @@ -77,7 +55,7 @@ def main() -> int: _ = print(f"update: {file_path}") if args.check: - _ = print(f"checked_version={args.version}") + _ = print(f"checked_version={target_version}") _ = print(f"files_changed={changed}") return 0 diff --git a/scripts/sync.py b/scripts/sync.py index e5cc33bf..5a4c5951 100644 --- a/scripts/sync.py +++ b/scripts/sync.py @@ -40,7 +40,11 @@ def _copy_if_changed(source: Path, target: Path) -> bool: def _sync_tree(source_dir: Path, target_dir: Path, prune: bool) -> int: changed = 0 source_files = { - p.relative_to(source_dir) for p in source_dir.rglob("*") if p.is_file() + p.relative_to(source_dir) + for p in source_dir.rglob("*") + if p.is_file() + and "__pycache__" not in p.parts + and not any(part.startswith(".") for part in p.parts) } for rel in sorted(source_files): changed += 1 if _copy_if_changed(source_dir / rel, target_dir / rel) else 0 @@ -79,6 +83,9 @@ def main() -> int: changed += _sync_tree( canonical_root / "scripts", project_root / "scripts", args.prune ) + changed += _sync_tree( + canonical_root / "libs", project_root / "libs", args.prune + ) fcntl.flock(lock_handle.fileno(), fcntl.LOCK_UN) print(f"files_changed={changed}") diff --git a/tests/unit/libs/versioning_tests.py b/tests/unit/libs/versioning_tests.py new file mode 100644 index 00000000..af7c85da --- /dev/null +++ b/tests/unit/libs/versioning_tests.py @@ -0,0 +1,63 @@ +from __future__ import annotations + +import importlib.util +from pathlib import Path +import sys +from typing import Any + + +def _load_module(module_name: str, relative_path: str) -> Any: + module_path = Path(__file__).resolve().parents[3] / relative_path + spec = importlib.util.spec_from_file_location(module_name, module_path) + assert spec is not None + assert spec.loader is not None + module = importlib.util.module_from_spec(spec) + sys.modules[spec.name] = module + spec.loader.exec_module(module) + return module + + +def test_parse_and_bump_semver() -> None: + mod = _load_module("libs_versioning_semver", "libs/versioning.py") + assert mod.parse_semver("1.2.3") == (1, 2, 3) + assert mod.bump_version("1.2.3", "patch") == "1.2.4" + assert mod.bump_version("1.2.3", "minor") == "1.3.0" + assert mod.bump_version("1.2.3", "major") == "2.0.0" + + +def test_release_tag_from_branch_patterns() -> None: + mod = _load_module("libs_versioning_release", "libs/versioning.py") + assert mod.release_tag_from_branch("0.11.0-dev") == "v0.11.0" + assert mod.release_tag_from_branch("release/0.12.3") == "v0.12.3" + assert mod.release_tag_from_branch("feature/abc") is None + + +def test_replace_project_version_updates_only_project_table() -> None: + mod = _load_module("libs_versioning_replace", "libs/versioning.py") + content = """ +[project] +name = "demo" +version = "0.11.0-dev" + +[tool.poetry.dependencies] +python = ">=3.13,<4.0" +flext-core = "0.11.0-dev" +""".strip() + updated, did_change = mod.replace_project_version(content, "0.11.0") + assert did_change is True + assert 'version = "0.11.0"' in updated + assert 'flext-core = "0.11.0-dev"' in updated + + +def test_current_workspace_version_reads_project_version(tmp_path: Path) -> None: + mod = _load_module("libs_versioning_current", "libs/versioning.py") + pyproject = tmp_path / "pyproject.toml" + _ = pyproject.write_text( + """ +[project] +name = "demo" +version = "0.10.0-dev" +""".strip(), + encoding="utf-8", + ) + assert mod.current_workspace_version(tmp_path) == "0.10.0" diff --git a/tests/unit/scripts/github/pr_manager_tests.py b/tests/unit/scripts/github/pr_manager_tests.py index 49a5af04..c7ff4536 100644 --- a/tests/unit/scripts/github/pr_manager_tests.py +++ b/tests/unit/scripts/github/pr_manager_tests.py @@ -238,3 +238,37 @@ def _fake_trigger_release_if_needed(repo_root: Path, head: str) -> None: assert calls[0] == ["gh", "pr", "merge", "7", "--squash"] assert calls[1] == ["gh", "pr", "update-branch", "7", "--rebase"] assert calls[2] == ["gh", "pr", "merge", "7", "--squash"] + + +def test_merge_returns_success_when_no_open_pr_for_head( + monkeypatch: pytest.MonkeyPatch, tmp_path: Path, capsys: pytest.CaptureFixture[str] +) -> None: + mod = _load_module("pr_manager_merge_no_open", "scripts/github/pr_manager.py") + + def _fake_open_pr_for_head( + _repo_root: Path, _head: str + ) -> dict[str, object] | None: + return None + + def _unexpected_run_stream_with_output( + _command: list[str], _cwd: Path + ) -> tuple[int, str]: + raise AssertionError("merge command should not run when PR is absent") + + monkeypatch.setattr(mod, "_open_pr_for_head", _fake_open_pr_for_head) + monkeypatch.setattr( + mod, "_run_stream_with_output", _unexpected_run_stream_with_output + ) + + exit_code = mod._merge_pr( + repo_root=tmp_path, + selector="0.11.0-dev", + head="0.11.0-dev", + method="squash", + auto=0, + delete_branch=0, + release_on_merge=1, + ) + + assert exit_code == 0 + assert "status=no-open-pr" in capsys.readouterr().out diff --git a/tests/unit/scripts/release/release_shared_and_run_tests.py b/tests/unit/scripts/release/release_shared_and_run_tests.py index bfc5292c..c046f8ab 100644 --- a/tests/unit/scripts/release/release_shared_and_run_tests.py +++ b/tests/unit/scripts/release/release_shared_and_run_tests.py @@ -62,3 +62,28 @@ def test_current_version_reads_project_table(tmp_path: Path) -> None: ) assert run_mod._current_version(tmp_path) == "0.10.0" + + +def test_phase_version_passes_dev_suffix_flag( + monkeypatch: pytest.MonkeyPatch, tmp_path: Path +) -> None: + run_mod = _load_module("release_run_phase_version_dev", "scripts/release/run.py") + recorded: list[list[str]] = [] + + def _fake_run_checked(command: list[str], cwd: Path | None = None) -> None: + _ = cwd + recorded.append(command) + + monkeypatch.setattr(run_mod, "run_checked", _fake_run_checked) + + run_mod._phase_version( + root=tmp_path, + version="0.12.0", + dry_run=False, + project_names=["flext-core"], + dev_suffix=True, + ) + + assert recorded + assert "--dev-suffix" in recorded[0] + assert "1" in recorded[0] diff --git a/tests/unit/scripts/sync_tests.py b/tests/unit/scripts/sync_tests.py new file mode 100644 index 00000000..74885b39 --- /dev/null +++ b/tests/unit/scripts/sync_tests.py @@ -0,0 +1,70 @@ +from __future__ import annotations + +import importlib.util +import sys +from pathlib import Path +from typing import Any + + +def _load_module(module_name: str, relative_path: str) -> Any: + module_path = Path(__file__).resolve().parents[3] / relative_path + spec = importlib.util.spec_from_file_location(module_name, module_path) + assert spec is not None + assert spec.loader is not None + module = importlib.util.module_from_spec(spec) + sys.modules[spec.name] = module + spec.loader.exec_module(module) + return module + + +def test_sync_tree_ignores_pycache_and_dot_paths(tmp_path: Path) -> None: + mod = _load_module("scripts_sync_ignore", "scripts/sync.py") + + source = tmp_path / "source" + target = tmp_path / "target" + _ = (source / "__pycache__").mkdir(parents=True) + _ = (source / ".hidden").mkdir(parents=True) + _ = (source / "nested").mkdir(parents=True) + _ = (source / "__pycache__" / "x.pyc").write_bytes(b"binary") + _ = (source / ".hidden" / "keep.txt").write_text("skip", encoding="utf-8") + _ = (source / "nested" / "tool.py").write_text("print('ok')\n", encoding="utf-8") + + changed = mod._sync_tree(source, target, prune=False) + assert changed == 1 + assert (target / "nested" / "tool.py").exists() + assert not (target / "__pycache__" / "x.pyc").exists() + assert not (target / ".hidden" / "keep.txt").exists() + + +def test_main_syncs_scripts_and_libs(tmp_path: Path, monkeypatch: Any) -> None: + mod = _load_module("scripts_sync_main", "scripts/sync.py") + + canonical = tmp_path / "canonical" + project = tmp_path / "project" + _ = (canonical / "scripts").mkdir(parents=True) + _ = (canonical / "libs").mkdir(parents=True) + _ = (project / "scripts").mkdir(parents=True) + _ = (canonical / "base.mk").write_text("BASE\n", encoding="utf-8") + _ = (canonical / "scripts" / "tool.py").write_text( + "print('sync')\n", encoding="utf-8" + ) + _ = (canonical / "libs" / "versioning.py").write_text( + "VALUE = 'v'\n", encoding="utf-8" + ) + + monkeypatch.setattr( + sys, + "argv", + [ + "sync.py", + "--project-root", + str(project), + "--canonical-root", + str(canonical), + ], + ) + + exit_code = mod.main() + assert exit_code == 0 + assert (project / "scripts" / "tool.py").exists() + assert (project / "libs" / "versioning.py").exists() From 76574658632b58517048c3fd61512063f7d1e137 Mon Sep 17 00:00:00 2001 From: Marlon Costa Date: Fri, 20 Feb 2026 17:17:00 -0300 Subject: [PATCH 34/34] chore: checkpoint pending 0.11.0-dev changes --- .github/workflows/release.yml | 4 ++-- Makefile | 23 ++--------------------- flexcore | 2 +- flext-api | 2 +- flext-auth | 2 +- flext-cli | 2 +- flext-core | 2 +- flext-db-oracle | 2 +- flext-dbt-ldap | 2 +- flext-dbt-ldif | 2 +- flext-dbt-oracle | 2 +- flext-dbt-oracle-wms | 2 +- flext-grpc | 2 +- flext-ldap | 2 +- flext-ldif | 2 +- flext-meltano | 2 +- flext-observability | 2 +- flext-oracle-oic | 2 +- flext-oracle-wms | 2 +- flext-plugin | 2 +- flext-quality | 2 +- flext-tap-ldap | 2 +- flext-tap-ldif | 2 +- flext-tap-oracle | 2 +- flext-tap-oracle-oic | 2 +- flext-tap-oracle-wms | 2 +- flext-target-ldap | 2 +- flext-target-ldif | 2 +- flext-target-oracle | 2 +- flext-target-oracle-oic | 2 +- flext-target-oracle-wms | 2 +- flext-web | 2 +- scripts/release/notes.py | 2 +- 33 files changed, 35 insertions(+), 54 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 7ea974c8..3c4c780e 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -72,9 +72,9 @@ jobs: - name: Setup workspace run: make setup - - name: Run release-ci pipeline + - name: Run release pipeline run: | - make release-ci \ + make release \ RELEASE_PHASE=validate,version,build,publish \ VERSION="${{ steps.release.outputs.version }}" \ TAG="${{ steps.release.outputs.tag }}" \ diff --git a/Makefile b/Makefile index 1b34e50e..9e2857aa 100644 --- a/Makefile +++ b/Makefile @@ -137,7 +137,7 @@ if [ -n "$$residual_venvs" ]; then \ fi endef -.PHONY: help setup upgrade build check security format docs test validate typings clean release release-ci pr +.PHONY: help setup upgrade build check security format docs test validate typings clean release pr help: ## Show simple workspace verbs $(Q)echo "FLEXT Workspace" @@ -156,7 +156,6 @@ help: ## Show simple workspace verbs $(Q)echo " test Run tests only in all projects" $(Q)echo " validate Run validate gates (FIX=1 auto-fix, VALIDATE_SCOPE=workspace for repo-level)" $(Q)echo " release Interactive workspace release orchestration" - $(Q)echo " release-ci Non-interactive release run for CI/tag workflows" $(Q)echo " pr Manage PRs for selected projects" $(Q)echo " typings Stub supply-chain + typing report (PROJECT/PROJECTS to scope)" $(Q)echo " clean Clean all projects" @@ -197,7 +196,7 @@ help: ## Show simple workspace verbs $(Q)echo " make test PROJECT=flext-api PYTEST_ARGS=\"-k unit\" FAIL_FAST=1" $(Q)echo " make validate VALIDATE_SCOPE=workspace" $(Q)echo " make release BUMP=minor" - $(Q)echo " make release-ci VERSION=0.11.0 TAG=v0.11.0 RELEASE_PHASE=all" + $(Q)echo " make release INTERACTIVE=0 CREATE_BRANCHES=0 VERSION=0.11.0 TAG=v0.11.0 RELEASE_PHASE=all" $(Q)echo " make pr PROJECT=flext-core PR_ACTION=status" $(Q)echo " make pr PROJECT=flext-core PR_ACTION=create PR_TITLE='release: 0.11.0-dev'" $(Q)echo " NOTE: External projects (not in .gitmodules) require manual clone." @@ -447,24 +446,6 @@ release: ## Interactive workspace release orchestration $(if $(TAG),--tag "$(TAG)",) \ $(if $(BUMP),--bump "$(BUMP)",) -release-ci: ## Non-interactive release run for CI/tag workflows - $(Q)$(ENSURE_NO_PROJECT_CONFLICT) - $(Q)$(ENFORCE_WORKSPACE_VENV) - $(Q)$(ENSURE_SELECTED_PROJECTS) - $(Q)$(ENSURE_PROJECTS_EXIST) - $(Q)python scripts/release/run.py \ - --root "$(CURDIR)" \ - --phase "$(RELEASE_PHASE)" \ - --interactive 0 \ - --dev-suffix "$(RELEASE_DEV_SUFFIX)" \ - --create-branches 0 \ - --projects $(SELECTED_PROJECTS) \ - $(if $(DRY_RUN),--dry-run "$(DRY_RUN)",) \ - $(if $(PUSH),--push "$(PUSH)",) \ - $(if $(VERSION),--version "$(VERSION)",) \ - $(if $(TAG),--tag "$(TAG)",) \ - $(if $(BUMP),--bump "$(BUMP)",) - pr: ## Manage pull requests for selected projects $(Q)$(ENSURE_NO_PROJECT_CONFLICT) $(Q)$(ENSURE_SELECTED_PROJECTS) diff --git a/flexcore b/flexcore index 8f841ad9..f927b8d6 160000 --- a/flexcore +++ b/flexcore @@ -1 +1 @@ -Subproject commit 8f841ad9780686803f403dcaee96bbc25c732a57 +Subproject commit f927b8d627fb24ff2bc68672fadb0b9ec72e8da3 diff --git a/flext-api b/flext-api index c26cc5fe..aff97f33 160000 --- a/flext-api +++ b/flext-api @@ -1 +1 @@ -Subproject commit c26cc5fe1fef4517e92909c50ddc1684f95c19e8 +Subproject commit aff97f33592a551acfa998da76ae2f54325b8c3e diff --git a/flext-auth b/flext-auth index 2205b4ae..a53c87bf 160000 --- a/flext-auth +++ b/flext-auth @@ -1 +1 @@ -Subproject commit 2205b4ae2877e1e3cdc0c8aa4cf8a9fbf86ff654 +Subproject commit a53c87bf8a58c3f2844a33a5acd0f7430be2553a diff --git a/flext-cli b/flext-cli index a48909a6..f38fe7bf 160000 --- a/flext-cli +++ b/flext-cli @@ -1 +1 @@ -Subproject commit a48909a6fa852b843dc642ac5445a7a1b6def49f +Subproject commit f38fe7bf97c43f9f4db6a4254aa3ac4ca196c59b diff --git a/flext-core b/flext-core index ecebd172..826e2e36 160000 --- a/flext-core +++ b/flext-core @@ -1 +1 @@ -Subproject commit ecebd1725b8196a1f1858f2a5da7595df8bd26e9 +Subproject commit 826e2e369e575c977f03f714840c47674cbbf4cc diff --git a/flext-db-oracle b/flext-db-oracle index a28cd6eb..2fce1377 160000 --- a/flext-db-oracle +++ b/flext-db-oracle @@ -1 +1 @@ -Subproject commit a28cd6ebd201f7db1feed49c176cf89b53706356 +Subproject commit 2fce137797a4178a3263e4b591f9d6e60a866d6f diff --git a/flext-dbt-ldap b/flext-dbt-ldap index d4be39fc..3d8c7b98 160000 --- a/flext-dbt-ldap +++ b/flext-dbt-ldap @@ -1 +1 @@ -Subproject commit d4be39fc598434a78b1b3857302378d7eea1fe22 +Subproject commit 3d8c7b98954440cec7d0ccbc02e992a8e480e7c6 diff --git a/flext-dbt-ldif b/flext-dbt-ldif index a60b0a86..858dbf55 160000 --- a/flext-dbt-ldif +++ b/flext-dbt-ldif @@ -1 +1 @@ -Subproject commit a60b0a8629fe8a5681e1b06e20155556307d3405 +Subproject commit 858dbf55a72df900786edffb9916c2037c5a01f2 diff --git a/flext-dbt-oracle b/flext-dbt-oracle index a9d377a6..79fbbcc8 160000 --- a/flext-dbt-oracle +++ b/flext-dbt-oracle @@ -1 +1 @@ -Subproject commit a9d377a69e4c1d6c553b5408d22442bb25e4a504 +Subproject commit 79fbbcc8014cd378243d46d6adf7ce0b06ca4b80 diff --git a/flext-dbt-oracle-wms b/flext-dbt-oracle-wms index e7e4df75..1fef95ad 160000 --- a/flext-dbt-oracle-wms +++ b/flext-dbt-oracle-wms @@ -1 +1 @@ -Subproject commit e7e4df755fca87a894e32e11467148774f3ad772 +Subproject commit 1fef95ad358193e84282b708f3bd4656094c59e6 diff --git a/flext-grpc b/flext-grpc index 611259da..ecf8c18f 160000 --- a/flext-grpc +++ b/flext-grpc @@ -1 +1 @@ -Subproject commit 611259da6f5358b2f4e64f1e1a44bd7867c8fe9f +Subproject commit ecf8c18f74712aea0c67ea8b33fbc99f01a95b76 diff --git a/flext-ldap b/flext-ldap index 07739926..aa0a6397 160000 --- a/flext-ldap +++ b/flext-ldap @@ -1 +1 @@ -Subproject commit 0773992654670d7b74310a32bec71393b00049b9 +Subproject commit aa0a639705e16cfd399aa68e58763d8f6b43d380 diff --git a/flext-ldif b/flext-ldif index 63f959b7..2e2f7467 160000 --- a/flext-ldif +++ b/flext-ldif @@ -1 +1 @@ -Subproject commit 63f959b715a57b6900f41a3d79f9a9a1a3cf7f5c +Subproject commit 2e2f7467a876f3be85ba86e897a405516d0ce104 diff --git a/flext-meltano b/flext-meltano index 144bb0ce..8ca8f6fd 160000 --- a/flext-meltano +++ b/flext-meltano @@ -1 +1 @@ -Subproject commit 144bb0cef987909da40ec16d7dcfe0ad2d690e63 +Subproject commit 8ca8f6fdece91d000fe01d77c09b58da541a8fc2 diff --git a/flext-observability b/flext-observability index 036dc83c..9de29722 160000 --- a/flext-observability +++ b/flext-observability @@ -1 +1 @@ -Subproject commit 036dc83c24eada8ee87dac8cc6adab1a06c12b8b +Subproject commit 9de297227b44039e4a759b110533392264c8bcd0 diff --git a/flext-oracle-oic b/flext-oracle-oic index b2530a1a..8db1a256 160000 --- a/flext-oracle-oic +++ b/flext-oracle-oic @@ -1 +1 @@ -Subproject commit b2530a1a14e48d84acc87a91b013a58db4af9ef1 +Subproject commit 8db1a256f920080f34101d7783ad5b28349ecb7e diff --git a/flext-oracle-wms b/flext-oracle-wms index 624cbd24..f2e92699 160000 --- a/flext-oracle-wms +++ b/flext-oracle-wms @@ -1 +1 @@ -Subproject commit 624cbd24158d7a0ac82242d6aea47f024cc8700e +Subproject commit f2e92699ca91a6bf480e3468988309852ed58984 diff --git a/flext-plugin b/flext-plugin index 5ce0a1fa..992f6c93 160000 --- a/flext-plugin +++ b/flext-plugin @@ -1 +1 @@ -Subproject commit 5ce0a1fa5dad3bcc36119e9666f1b81a362aa509 +Subproject commit 992f6c9350d4c9bf45a09a2d6564c136d6b73a9d diff --git a/flext-quality b/flext-quality index 1a66af6b..620da0fb 160000 --- a/flext-quality +++ b/flext-quality @@ -1 +1 @@ -Subproject commit 1a66af6bfed4fe10902c10259059681917ce1d94 +Subproject commit 620da0fb48540e79e3f4c13b9e54f59461da8371 diff --git a/flext-tap-ldap b/flext-tap-ldap index acdb8160..dcd83d34 160000 --- a/flext-tap-ldap +++ b/flext-tap-ldap @@ -1 +1 @@ -Subproject commit acdb81601645c3b3757f4d10d5692bca911ea084 +Subproject commit dcd83d340664a5d94f6da995d54e2373a561699f diff --git a/flext-tap-ldif b/flext-tap-ldif index 02f844ae..f7db4292 160000 --- a/flext-tap-ldif +++ b/flext-tap-ldif @@ -1 +1 @@ -Subproject commit 02f844aedca8664ad02cf6e2b030c71c7127ad71 +Subproject commit f7db4292d7bf10dc9b45e1997d95ae692170fbcb diff --git a/flext-tap-oracle b/flext-tap-oracle index bd27abc2..cfe628ab 160000 --- a/flext-tap-oracle +++ b/flext-tap-oracle @@ -1 +1 @@ -Subproject commit bd27abc2fb3a65b9ec666d83b86f3ea73f7bd5b5 +Subproject commit cfe628ab7e161caa37cfbfde90a0c1ade0900b18 diff --git a/flext-tap-oracle-oic b/flext-tap-oracle-oic index a1e6d20a..2f2082c1 160000 --- a/flext-tap-oracle-oic +++ b/flext-tap-oracle-oic @@ -1 +1 @@ -Subproject commit a1e6d20ae6e9d9f0e4330b3090b385ac0a798877 +Subproject commit 2f2082c147fcd269f0e8088a58e1d2a63d12e73d diff --git a/flext-tap-oracle-wms b/flext-tap-oracle-wms index dfd3ff4e..b6f21496 160000 --- a/flext-tap-oracle-wms +++ b/flext-tap-oracle-wms @@ -1 +1 @@ -Subproject commit dfd3ff4e07bcb16668158af320f3ec1e4c844201 +Subproject commit b6f214963569649c941b02ca5c3a66c205baca99 diff --git a/flext-target-ldap b/flext-target-ldap index 7866ff73..47ba4841 160000 --- a/flext-target-ldap +++ b/flext-target-ldap @@ -1 +1 @@ -Subproject commit 7866ff73dc1f6006d1f9ed3faeaf7c944bdf6f72 +Subproject commit 47ba484126af6898339622c058710d0300a838a5 diff --git a/flext-target-ldif b/flext-target-ldif index cd6fd3a5..22c26128 160000 --- a/flext-target-ldif +++ b/flext-target-ldif @@ -1 +1 @@ -Subproject commit cd6fd3a5e6c32b6ccf64ad37ccb2ac0766179bb1 +Subproject commit 22c26128a80f493797e73880dd91b8c490064108 diff --git a/flext-target-oracle b/flext-target-oracle index 6e07187b..ffa85004 160000 --- a/flext-target-oracle +++ b/flext-target-oracle @@ -1 +1 @@ -Subproject commit 6e07187b55e24c13dc792851724594d4db219bfa +Subproject commit ffa850049b2c5f00ebf17095fa9420fe5c599464 diff --git a/flext-target-oracle-oic b/flext-target-oracle-oic index 1304beb3..2ea29910 160000 --- a/flext-target-oracle-oic +++ b/flext-target-oracle-oic @@ -1 +1 @@ -Subproject commit 1304beb330da1c79fdc7694e2db9dfa8e5af5669 +Subproject commit 2ea299104d57e31f10b2bb1331c757f6bbc4477d diff --git a/flext-target-oracle-wms b/flext-target-oracle-wms index a1564f1c..3fbb81e1 160000 --- a/flext-target-oracle-wms +++ b/flext-target-oracle-wms @@ -1 +1 @@ -Subproject commit a1564f1ca7760eb3da1a792f9afd08dc74ddb6a2 +Subproject commit 3fbb81e10c85bd9899292f4c4fa11a44aefc7de8 diff --git a/flext-web b/flext-web index 2fba7fa5..1843ce45 160000 --- a/flext-web +++ b/flext-web @@ -1 +1 @@ -Subproject commit 2fba7fa504b1a435e37d413478718dc027960b01 +Subproject commit 1843ce4515865c7292c2aedd6b9b363bf093d726 diff --git a/scripts/release/notes.py b/scripts/release/notes.py index 0fa8c547..5167ffa0 100644 --- a/scripts/release/notes.py +++ b/scripts/release/notes.py @@ -91,7 +91,7 @@ def main() -> int: "", "## Verification", "", - "- make release-ci RELEASE_PHASE=all", + "- make release INTERACTIVE=0 CREATE_BRANCHES=0 RELEASE_PHASE=all", "- make validate VALIDATE_SCOPE=workspace", "- make build", ])