Spaces:
Sleeping
Sleeping
| #!/usr/bin/env python3 | |
| """ | |
| refresh_sources.py — Standalone refresh script in Python | |
| Actions: | |
| • git clone (first run) or git pull (subsequent) for each source | |
| • Inject auth token if `auth_env_var` is defined in config | |
| • CREATE OR REPLACE VIEW for each defined view | |
| """ | |
| import os | |
| import sys | |
| import json | |
| import yaml | |
| import urllib.request | |
| import urllib.error | |
| import subprocess | |
| # Allow local testing gracefully | |
| CONFIG = "/app/sources.yaml" if os.path.exists("/app/sources.yaml") else "sources.yaml" | |
| USER_FILES = "/app/ch/user_files" if os.path.exists("/app") else "user_files" | |
| CH_URL = "http://127.0.0.1:8123" | |
| def run_subprocess(cmd, cwd=None, env=None): | |
| try: | |
| # We use Popen to stream stderr to the console for progress bars | |
| # while still capturing stdout for return values if needed. | |
| process = subprocess.Popen( | |
| cmd, cwd=cwd, env=env, | |
| stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True | |
| ) | |
| # Stream stderr in real time | |
| stderr_output = [] | |
| while True: | |
| line = process.stderr.readline() | |
| if not line and process.poll() is not None: | |
| break | |
| if line: | |
| sys.stderr.write(line) | |
| sys.stderr.flush() | |
| stderr_output.append(line) | |
| stdout, _ = process.communicate() | |
| if process.returncode == 0: | |
| return stdout.strip(), True | |
| else: | |
| return "".join(stderr_output).strip(), False | |
| except Exception as e: | |
| return str(e), False | |
| def execute_clickhouse_query(sql): | |
| req = urllib.request.Request(CH_URL, data=sql.encode('utf-8'), method='POST') | |
| try: | |
| resp = urllib.request.urlopen(req, timeout=10) | |
| return resp.read().decode('utf-8'), True | |
| except urllib.error.URLError as e: | |
| return str(e), False | |
| def main(): | |
| mode = "full" | |
| if len(sys.argv) > 1: | |
| mode = sys.argv[1] | |
| if not os.path.exists(CONFIG): | |
| print(json.dumps({"error": f"Config not found at {CONFIG}"})) | |
| sys.exit(1) | |
| with open(CONFIG, "r") as f: | |
| config_data = yaml.safe_load(f) | |
| sources = config_data.get("sources", []) | |
| result = { | |
| "mode": mode, | |
| "source_count": len(sources), | |
| "sources": [] | |
| } | |
| env_vars = os.environ.copy() | |
| for source in sources: | |
| name = source.get("name", "unknown") | |
| repo_url = source.get("repo_url") | |
| local_dir = source.get("local_dir") | |
| clone_depth = str(source.get("clone_depth", 1)) | |
| branch = source.get("branch", "") | |
| auth_env_var = source.get("auth_env_var", "") | |
| target_dir = os.path.join(USER_FILES, local_dir) | |
| # Build git clone/pull options | |
| auth_flag = [] | |
| if auth_env_var and auth_env_var in env_vars: | |
| token = env_vars[auth_env_var] | |
| auth_flag = ["-c", f"http.extraHeader=Authorization: Bearer {token}"] | |
| branch_flag = [] | |
| if branch: | |
| branch_flag = ["--branch", branch] | |
| git_status = "unknown" | |
| # Git Sync | |
| if not os.path.isdir(target_dir): | |
| cmd = ["git", "clone", "--depth", clone_depth] + branch_flag + auth_flag + [repo_url, target_dir] | |
| output, success = run_subprocess(cmd) | |
| git_status = "cloned" if success else f"error_clone: {output}" | |
| # Retrieve real files for Git LFS pointers natively used by Hugging Face Datasets | |
| if success: | |
| cmd_lfs = ["git", "lfs", "pull"] | |
| out_lfs, suc_lfs = run_subprocess(cmd_lfs, cwd=target_dir) | |
| if not suc_lfs: | |
| git_status = f"error_lfs_pull: {out_lfs}" | |
| else: | |
| # We want to fetch and hard reset | |
| cmd_fetch = ["git"] + auth_flag + ["fetch", "--depth", "1", "origin"] | |
| output_f, success_f = run_subprocess(cmd_fetch, cwd=target_dir) | |
| if success_f: | |
| # get current branch | |
| rev_cmd = ["git", "rev-parse", "--abbrev-ref", "HEAD"] | |
| curr_branch, _ = run_subprocess(rev_cmd, cwd=target_dir) | |
| reset_cmd = ["git", "reset", "--hard", f"origin/{curr_branch}"] | |
| output_r, success_r = run_subprocess(reset_cmd, cwd=target_dir) | |
| git_status = "pulled" if success_r else f"error_reset: {output_r}" | |
| else: | |
| git_status = f"error_fetch: {output_f}" | |
| latest_commit, _ = run_subprocess(["git", "log", "-1", "--format=%h %s"], cwd=target_dir) | |
| if not latest_commit: | |
| latest_commit = "unknown" | |
| source_info = { | |
| "name": name, | |
| "git_status": git_status, | |
| "latest_commit": latest_commit | |
| } | |
| # View Creation | |
| if mode == "full": | |
| views_info = [] | |
| views = source.get("views", []) | |
| for view in views: | |
| view_name = view.get("view_name") | |
| file_glob = view.get("file_glob") | |
| format_ = view.get("format", "Parquet") | |
| columns = view.get("columns", []) | |
| full_glob = f"{local_dir}/{file_glob}" | |
| if columns: | |
| cols_str = ", ".join(columns) | |
| select_clause = f"SELECT {cols_str}" | |
| else: | |
| select_clause = "SELECT *" | |
| sql = f"CREATE OR REPLACE VIEW {view_name} AS {select_clause} FROM file('{full_glob}', {format_})" | |
| _, success = execute_clickhouse_query(sql) | |
| view_status = "ok" if success else "error" | |
| views_info.append({ | |
| "name": view_name, | |
| "glob": full_glob, | |
| "select": select_clause, | |
| "status": view_status | |
| }) | |
| source_info["views"] = views_info | |
| result["sources"].append(source_info) | |
| print(json.dumps(result, indent=2)) | |
| if __name__ == "__main__": | |
| main() | |