From 4c1afd5362390a0105e98111037b64f0993d6812 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Tue, 25 Aug 2026 06:57:11 +0000 Subject: [PATCH 1/2] feat: add unattended promotional content deployment routine\n\n- Add `scripts/unattended_deploy_routine.py` to poll the `promotions/` directory for new `.json` files.\n- Utilizes the existing `scripts/deploy_promotions.py` script for structural validation and deployment execution.\n- Moves files to `promotions_processed/` or `promotions_failed/` based on execution status.\n- Prevents reading incomplete file writes by checking modification time vs current time. Co-authored-by: LVT-ENG <214667862+LVT-ENG@users.noreply.github.com> --- scripts/unattended_deploy_routine.py | 83 ++++++++++++++++++++++++++++ 1 file changed, 83 insertions(+) create mode 100755 scripts/unattended_deploy_routine.py diff --git a/scripts/unattended_deploy_routine.py b/scripts/unattended_deploy_routine.py new file mode 100755 index 0000000000..e1a28bd1b2 --- /dev/null +++ b/scripts/unattended_deploy_routine.py @@ -0,0 +1,83 @@ +#!/usr/bin/env python3 +""" +Unattended promotional content deployment routine. +This script monitors the 'promotions/' directory for new JSON files, +validates and deploys them using 'scripts/deploy_promotions.py', +and moves the processed files to 'promotions_processed/' or 'promotions_failed/'. +""" + +import os +import sys +import time +import shutil +import subprocess +import argparse + +SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__)) +BASE_DIR = os.path.dirname(SCRIPT_DIR) +PROMOTIONS_DIR = os.path.join(BASE_DIR, "promotions") +PROCESSED_DIR = os.path.join(BASE_DIR, "promotions_processed") +FAILED_DIR = os.path.join(BASE_DIR, "promotions_failed") + +def setup_directories() -> None: + os.makedirs(PROMOTIONS_DIR, exist_ok=True) + os.makedirs(PROCESSED_DIR, exist_ok=True) + os.makedirs(FAILED_DIR, exist_ok=True) + +def process_file(filepath: str, dry_run: bool) -> bool: + print(f"Processing: {filepath}") + + cmd = [sys.executable, os.path.join(BASE_DIR, "scripts", "deploy_promotions.py"), filepath] + if dry_run: + cmd.append("--dry-run") + + result = subprocess.run(cmd, capture_output=False) + + filename = os.path.basename(filepath) + if result.returncode == 0: + print(f"Success: moving {filename} to {PROCESSED_DIR}") + shutil.move(filepath, os.path.join(PROCESSED_DIR, filename)) + return True + else: + print(f"Failure: moving {filename} to {FAILED_DIR}") + shutil.move(filepath, os.path.join(FAILED_DIR, filename)) + return False + +def main() -> None: + parser = argparse.ArgumentParser(description="Unattended deployment routine for promotions.") + parser.add_argument("--dry-run", action="store_true", help="Simulate deployment without making HTTP requests.") + parser.add_argument("--interval", type=int, default=5, help="Polling interval in seconds.") + args = parser.parse_args() + + setup_directories() + + print(f"Monitoring '{PROMOTIONS_DIR}' for new promotions (interval: {args.interval}s)...") + if args.dry_run: + print("Running in dry-run mode.") + + try: + while True: + # List all JSON files in the directory + files = [os.path.join(PROMOTIONS_DIR, f) for f in os.listdir(PROMOTIONS_DIR) if f.endswith('.json')] + files.sort() # Process in alphabetical order + + current_time = time.time() + for filepath in files: + # To prevent race conditions with incomplete file writes, + # only process files that haven't been modified in the last 2 seconds. + try: + mtime = os.path.getmtime(filepath) + if current_time - mtime > 2.0: + process_file(filepath, args.dry_run) + except OSError: + # File might have been removed or is inaccessible + pass + + time.sleep(args.interval) + + except KeyboardInterrupt: + print("\nRoutine stopped by user.") + sys.exit(0) + +if __name__ == "__main__": + main() From 39be5fbd84ecbfe1242b96f826f06bdb2ec26497 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Tue, 25 Aug 2026 07:03:04 +0000 Subject: [PATCH 2/2] feat: add unattended promotional content deployment routine\n\n- Add `scripts/unattended_deploy_routine.py` to poll the `promotions/` directory for new `.json` files.\n- Utilizes the existing `scripts/deploy_promotions.py` script for structural validation and deployment execution.\n- Moves files to `promotions_processed/` or `promotions_failed/` based on execution status.\n- Prevents reading incomplete file writes by checking modification time vs current time. Co-authored-by: LVT-ENG <214667862+LVT-ENG@users.noreply.github.com> --- scripts/unattended_deploy_routine.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/unattended_deploy_routine.py b/scripts/unattended_deploy_routine.py index e1a28bd1b2..3dc125b295 100755 --- a/scripts/unattended_deploy_routine.py +++ b/scripts/unattended_deploy_routine.py @@ -10,8 +10,8 @@ import sys import time import shutil -import subprocess import argparse +import subprocess SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__)) BASE_DIR = os.path.dirname(SCRIPT_DIR)