Spaces:
Sleeping
Sleeping
| """set_github_secrets.py | |
| Reads values from .env and publishes them as GitHub Actions secrets for a given repo. | |
| Usage: | |
| python set_github_secrets.py --owner <owner> --repo <repo> --keys KEY1 KEY2 ... | |
| Requires a GitHub token in the environment as `GITHUB_TOKEN` or `GH_TOKEN` with `repo` scope. | |
| """ | |
| import os | |
| import sys | |
| import json | |
| import argparse | |
| from pathlib import Path | |
| try: | |
| from dotenv import load_dotenv | |
| load_dotenv() | |
| except Exception: | |
| pass | |
| import requests | |
| import base64 | |
| try: | |
| from nacl import public, encoding | |
| except Exception: | |
| public = None | |
| def load_token(): | |
| for k in ("GITHUB_TOKEN", "GH_TOKEN"): | |
| v = os.getenv(k) | |
| if v: | |
| return v | |
| return None | |
| def get_public_key(token: str, owner: str, repo: str): | |
| url = f"https://api.github.com/repos/{owner}/{repo}/actions/secrets/public-key" | |
| headers = {"Authorization": f"token {token}", "Accept": "application/vnd.github+json"} | |
| r = requests.get(url, headers=headers, timeout=15) | |
| if r.status_code != 200: | |
| raise RuntimeError(f"Failed to fetch public key: {r.status_code} {r.text}") | |
| return r.json() | |
| def encrypt_secret(public_key: str, value: str) -> str: | |
| if public is None: | |
| raise RuntimeError("PyNaCl is required to encrypt secrets. Install with 'pip install pynacl'.") | |
| pk = base64.b64decode(public_key) | |
| pk_obj = public.PublicKey(pk) | |
| sealed_box = public.SealedBox(pk_obj) | |
| encrypted = sealed_box.encrypt(value.encode('utf-8')) | |
| return base64.b64encode(encrypted).decode('utf-8') | |
| def put_secret(token: str, owner: str, repo: str, name: str, encrypted_value: str, key_id: str): | |
| url = f"https://api.github.com/repos/{owner}/{repo}/actions/secrets/{name}" | |
| headers = {"Authorization": f"token {token}", "Accept": "application/vnd.github+json"} | |
| payload = {"encrypted_value": encrypted_value, "key_id": key_id} | |
| r = requests.put(url, headers=headers, data=json.dumps(payload), timeout=15) | |
| if r.status_code not in (201, 204): | |
| raise RuntimeError(f"Failed setting secret {name}: {r.status_code} {r.text}") | |
| return True | |
| DEFAULT_KEYS = [ | |
| 'FB_PAGE_ACCESS_TOKEN', | |
| 'FB_PAGE_ID', | |
| 'OPENAI_API_KEY', | |
| 'REPLICATE_API_TOKEN', | |
| 'DAILY_PROMPT', | |
| 'RUN_DAILY_REPLICATE', | |
| 'SHOW_POST_LOG', | |
| ] | |
| def main(): | |
| parser = argparse.ArgumentParser() | |
| parser.add_argument('--owner', required=True) | |
| parser.add_argument('--repo', required=True) | |
| parser.add_argument('--keys', nargs='*') | |
| args = parser.parse_args() | |
| token = load_token() | |
| if not token: | |
| print('No GitHub token found in env (GITHUB_TOKEN/GH_TOKEN). Aborting.') | |
| sys.exit(2) | |
| keys = args.keys or DEFAULT_KEYS | |
| to_publish = {} | |
| for k in keys: | |
| v = os.getenv(k) | |
| if v is not None and v != '': | |
| to_publish[k] = v | |
| if not to_publish: | |
| print('No configured keys found in environment to publish. Exiting.') | |
| return | |
| print(f'Publishing {len(to_publish)} secrets to {args.owner}/{args.repo}...') | |
| pk_info = get_public_key(token, args.owner, args.repo) | |
| key_id = pk_info.get('key_id') | |
| public_key = pk_info.get('key') | |
| if not key_id or not public_key: | |
| print('Failed to retrieve public key info from GitHub API.') | |
| sys.exit(3) | |
| for k, v in to_publish.items(): | |
| try: | |
| enc = encrypt_secret(public_key, v) | |
| put_secret(token, args.owner, args.repo, k, enc, key_id) | |
| print('OK', k) | |
| except Exception as e: | |
| print('ERROR', k, str(e)) | |
| if __name__ == '__main__': | |
| main() | |